diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7c6ee85a6b2be30d5b669fd7fbbb49bc2a15076e..afa5166181315ebec3edaa87069001b2e1eaa5b1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -439,7 +439,7 @@ publish_on_gitlab: - if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH) || $CI_COMMIT_TAG before_script: - apt-get update - - apt-get install ansible -y + - apt-get install ansible openssh-client -y # Use Gitlab protected variable to provide key - echo "$DEPLOY_KEY" > id_rsa - chmod 400 id_rsa diff --git a/CDB/LOFAR_ConfigDb.json b/CDB/LOFAR_ConfigDb.json index 2b99319ea886c6427448b994d99d3c4781337d34..13faba11e61c6ef8eea6561ae882ac41309b4865 100644 --- a/CDB/LOFAR_ConfigDb.json +++ b/CDB/LOFAR_ConfigDb.json @@ -59,6 +59,13 @@ "STAT/RCU2H/1" ] } + }, + "STAT/AntennaField/LBA": { + "properties": { + "RECV_devices": [ + "STAT/RCU2L/1" + ] + } } } } diff --git a/CDB/README.md b/CDB/README.md index 4ad24de4f8425a304082d41801dd3fdecab0d6b0..ff36bc232d577107b8a4b664bb73d3add9066b24 100644 --- a/CDB/README.md +++ b/CDB/README.md @@ -12,6 +12,7 @@ The following files are provided: | File | Description | Usage | |--------------------------------------------|-------------------------------------------------------------|---------------------| | `LOFAR_ConfigDb.json` | Generic base configuration, registering all of the devices. | Always | +| `hierarchies/power.json` | Power Hierarchy configuration. | Always | | `test_environment_ConfigDb.json` | Base delta for the unit- and integration test suites. | Tests & development | | `stations/simulators_ConfigDb.json` | A "station" configuration that points to our simulators. | Tests & development | | `stations/dummy_positions_ConfigDb.json` | An antenna configuration, just to have one (it's CS001). | Tests & development | diff --git a/CDB/hierarchies/power.json b/CDB/hierarchies/power.json new file mode 100644 index 0000000000000000000000000000000000000000..240ff71fc66b0e028737694c9f2ea7d85870279d --- /dev/null +++ b/CDB/hierarchies/power.json @@ -0,0 +1,277 @@ +{ + "servers": { + "StationManager": { + "STAT": { + "StationManager": { + "STAT/StationManager/1": { + "properties": { + "Power_Children": [ + "STAT/PSOC/1", + "STAT/PCON/1", + "STAT/Configuration/1", + "STAT/ObservationControl/1", + "STAT/TemperatureManager/1", + "STAT/Docker/1" + ] + } + } + } + } + }, + "PSOC": { + "STAT": { + "PSOC": { + "STAT/PSOC/1": { + "properties": { + "Power_Parent": [ + "STAT/StationManager/1" + ], + "Power_Children": [ + "STAT/CCD/1", + "STAT/SDP/1" + ] + } + } + } + } + }, + "PCON": { + "STAT": { + "PCON": { + "STAT/PCON/1": { + "properties": { + "Power_Parent": [ + "STAT/StationManager/1" + ], + "Power_Children": [ + "STAT/APSPU/1", + "STAT/AntennaField/HBA" + ] + } + } + } + } + }, + "APSCT": { + "STAT": { + "APSCT": { + "STAT/APSCT/1": { + "properties": { + "Power_Parent": [ + "STAT/APSPU/1" + ] + } + } + } + } + }, + "CCD": { + "STAT": { + "CCD": { + "STAT/CCD/1": { + "properties": { + "Power_Parent": [ + "STAT/PSOC/1" + ] + } + } + } + } + }, + "AntennaField": { + "STAT": { + "AntennaField": { + "STAT/AntennaField/HBA": { + "properties": { + "Power_Parent": [ + "STAT/PCON/1" + ] + } + } + } + } + }, + "APSPU": { + "STAT": { + "APSPU": { + "STAT/APSPU/1": { + "properties": { + "Power_Parent": [ + "STAT/PCON/1" + ], + "Power_Children": [ + "STAT/APSCT/1", + "STAT/UNB2/1", + "STAT/RCU2H/1", + "STAT/RCU2L/1" + ] + } + } + } + } + }, + "RCU2H": { + "STAT": { + "RCU2H": { + "STAT/RCU2H/1": { + "properties": { + "Power_Parent": [ + "STAT/APSPU/1" + ] + } + } + } + } + }, + "RCU2L": { + "STAT": { + "RCU2L": { + "STAT/RCU2L/1": { + "properties": { + "Power_Parent": [ + "STAT/APSPU/1" + ] + } + } + } + } + }, + "SDP": { + "STAT": { + "SDP": { + "STAT/SDP/1": { + "properties": { + "Power_Parent": [ + "STAT/PSOC/1" + ], + "Power_Children": [ + "STAT/Beamlet/1", + "STAT/BST/1", + "STAT/SST/1", + "STAT/XST/1" + ] + } + } + } + } + }, + "UNB2": { + "STAT": { + "UNB2": { + "STAT/UNB2/1": { + "properties": { + "Power_Parent": [ + "STAT/APSPU/1" + ] + } + } + } + } + }, + "Beamlet": { + "STAT": { + "Beamlet": { + "STAT/Beamlet/1": { + "properties": { + "Power_Parent": [ + "STAT/SDP/1" + ] + } + } + } + } + }, + "BST": { + "STAT": { + "BST": { + "STAT/BST/1": { + "properties": { + "Power_Parent": [ + "STAT/SDP/1" + ] + } + } + } + } + }, + "SST": { + "STAT": { + "SST": { + "STAT/SST/1": { + "properties": { + "Power_Parent": [ + "STAT/SDP/1" + ] + } + } + } + } + }, + "XST": { + "STAT": { + "XST": { + "STAT/XST/1": { + "properties": { + "Power_Parent": [ + "STAT/SDP/1" + ] + } + } + } + } + }, + "Configuration": { + "STAT": { + "Configuration": { + "STAT/Configuration/1": { + "properties": { + "Power_Parent": [ + "STAT/StationManager/1" + ] + } + } + } + } + }, + "ObservationControl": { + "STAT": { + "ObservationControl": { + "STAT/ObservationControl/1": { + "properties": { + "Power_Parent": [ + "STAT/StationManager/1" + ] + } + } + } + } + }, + "TemperatureManager": { + "STAT": { + "TemperatureManager": { + "STAT/TemperatureManager/1": { + "properties": { + "Power_Parent": [ + "STAT/StationManager/1" + ] + } + } + } + } + }, + "Docker": { + "STAT": { + "Docker": { + "STAT/Docker/1": { + "properties": { + "Power_Parent": [ + "STAT/StationManager/1" + ] + } + } + } + } + } + } + } + \ No newline at end of file diff --git a/CDB/stations/DTS_ConfigDb.json b/CDB/stations/DTS_ConfigDb.json index eed2c42d30b7ba16e501493fe80e6832127cb049..921c3e3a34312967ff0e088f055c5fff794450e8 100644 --- a/CDB/stations/DTS_ConfigDb.json +++ b/CDB/stations/DTS_ConfigDb.json @@ -23,6 +23,41 @@ "properties": { "Initialise_Hardware": [ "True" + ], + "Device_Names": [ + "STAT/Docker/1", + "STAT/Configuration/1", + "STAT/PSOC/1", + "STAT/PCON/1", + "STAT/APSPU/L0", + "STAT/APSCT/L0", + "STAT/RCU2L/L0", + "STAT/UNB2/L0", + "STAT/APSPU/L1", + "STAT/APSCT/L1", + "STAT/RCU2L/L1", + "STAT/UNB2/L1", + "STAT/APSPU/H0", + "STAT/APSCT/H0", + "STAT/RCU2H/H0", + "STAT/UNB2/H0", + "STAT/CCD/1", + "STAT/SDP/HBA", + "STAT/BST/HBA", + "STAT/SST/HBA", + "STAT/XST/HBA", + "STAT/Beamlet/HBA", + "STAT/AntennaField/HBA", + "STAT/TileBeam/HBA", + "STAT/DigitalBeam/HBA", + "STAT/SDP/LBA", + "STAT/BST/LBA", + "STAT/SST/LBA", + "STAT/XST/LBA", + "STAT/Beamlet/LBA", + "STAT/AntennaField/LBA", + "STAT/DigitalBeam/LBA", + "STAT/TemperatureManager/1" ] } } @@ -32,10 +67,36 @@ "APSCT": { "STAT": { "APSCT": { - "STAT/APSCT/1": { + "STAT/APSCT/L0": { "properties": { "OPC_Server_Name": [ - "10.99.0.101" + "10.99.0.100" + ], + "OPC_Server_Port": [ + "4843" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + }, + "STAT/APSCT/L1": { + "properties": { + "OPC_Server_Name": [ + "10.99.1.100" + ], + "OPC_Server_Port": [ + "4843" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + }, + "STAT/APSCT/H0": { + "properties": { + "OPC_Server_Name": [ + "10.99.2.100" ], "OPC_Server_Port": [ "4843" @@ -51,10 +112,36 @@ "APSPU": { "STAT": { "APSPU": { - "STAT/APSPU/1": { + "STAT/APSPU/L0": { "properties": { "OPC_Server_Name": [ - "10.99.0.101" + "10.99.0.100" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + }, + "STAT/APSPU/L1": { + "properties": { + "OPC_Server_Name": [ + "10.99.1.100" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + }, + "STAT/APSPU/H0": { + "properties": { + "OPC_Server_Name": [ + "10.99.2.100" ], "OPC_Server_Port": [ "4842" @@ -83,8 +170,27 @@ "DigitalBeam": { "STAT": { "DigitalBeam": { + "STAT/DigitalBeam/LBA": { + "properties": { + "AntennaField_Device": [ + "STAT/AntennaField/LBA" + ], + "Beamlet_Device": [ + "STAT/Beamlet/LBA" + ], + "Tracking_enabled_RW_default": [ + "False" + ] + } + }, "STAT/DigitalBeam/HBA": { "properties": { + "AntennaField_Device": [ + "STAT/AntennaField/HBA" + ], + "Beamlet_Device": [ + "STAT/Beamlet/HBA" + ], "Tracking_enabled_RW_default": [ "False" ] @@ -96,7 +202,7 @@ "Beamlet": { "STAT": { "Beamlet": { - "STAT/Beamlet/1": { + "STAT/Beamlet/LBA": { "properties": { "OPC_Server_Name": [ "10.99.0.250" @@ -107,6 +213,181 @@ "OPC_Time_Out": [ "5.0" ], + "FPGA_beamlet_output_hdr_eth_source_mac_RW_default": [ + "00:22:86:08:00:00", + "00:22:86:08:00:01", + "00:22:86:08:00:02", + "00:22:86:08:00:03", + "00:22:86:08:01:00", + "00:22:86:08:01:01", + "00:22:86:08:01:02", + "00:22:86:08:01:03", + "00:22:86:08:02:00", + "00:22:86:08:02:01", + "00:22:86:08:02:02", + "00:22:86:08:02:03", + "00:22:86:08:03:00", + "00:22:86:08:03:01", + "00:22:86:08:03:02", + "00:22:86:08:03:03" + ], + "FPGA_beamlet_output_hdr_ip_source_address_RW_default": [ + "192.168.0.1", + "192.168.0.2", + "192.168.0.3", + "192.168.0.4", + "192.168.1.1", + "192.168.1.2", + "192.168.1.3", + "192.168.1.4", + "192.168.2.1", + "192.168.2.2", + "192.168.2.3", + "192.168.2.4", + "192.168.3.1", + "192.168.3.2", + "192.168.3.3", + "192.168.3.4" + ], + "FPGA_beamlet_output_hdr_udp_source_port_RW_default": [ + "53248", + "53249", + "53250", + "53251", + "53252", + "53253", + "53254", + "53255", + "53256", + "53257", + "53258", + "53259", + "53260", + "53261", + "53262", + "53263" + ], + "FPGA_beamlet_output_hdr_eth_destination_mac_RW_default": [ + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc", + "ec:0d:9a:bf:f2:dc" + ], + "FPGA_beamlet_output_hdr_ip_destination_address_RW_default": [ + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250", + "192.168.1.250" + ], + "FPGA_beamlet_output_hdr_udp_destination_port_RW_default": [ + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001" + ] + } + }, + "STAT/Beamlet/HBA": { + "properties": { + "OPC_Server_Name": [ + "10.99.0.250" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ], + "FPGA_beamlet_output_hdr_eth_source_mac_RW_default": [ + "00:22:86:08:00:00", + "00:22:86:08:00:01", + "00:22:86:08:00:02", + "00:22:86:08:00:03", + "00:22:86:08:01:00", + "00:22:86:08:01:01", + "00:22:86:08:01:02", + "00:22:86:08:01:03", + "00:22:86:08:02:00", + "00:22:86:08:02:01", + "00:22:86:08:02:02", + "00:22:86:08:02:03", + "00:22:86:08:03:00", + "00:22:86:08:03:01", + "00:22:86:08:03:02", + "00:22:86:08:03:03" + ], + "FPGA_beamlet_output_hdr_ip_source_address_RW_default": [ + "192.168.0.1", + "192.168.0.2", + "192.168.0.3", + "192.168.0.4", + "192.168.1.1", + "192.168.1.2", + "192.168.1.3", + "192.168.1.4", + "192.168.2.1", + "192.168.2.2", + "192.168.2.3", + "192.168.2.4", + "192.168.3.1", + "192.168.3.2", + "192.168.3.3", + "192.168.3.4" + ], + "FPGA_beamlet_output_hdr_udp_source_port_RW_default": [ + "53248", + "53249", + "53250", + "53251", + "53252", + "53253", + "53254", + "53255", + "53256", + "53257", + "53258", + "53259", + "53260", + "53261", + "53262", + "53263" + ], "FPGA_beamlet_output_hdr_eth_destination_mac_RW_default": [ "ec:0d:9a:bf:f2:dc", "ec:0d:9a:bf:f2:dc", @@ -142,6 +423,24 @@ "192.168.1.250", "192.168.1.250", "192.168.1.250" + ], + "FPGA_beamlet_output_hdr_udp_destination_port_RW_default": [ + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001", + "10001" ] } } @@ -157,7 +456,7 @@ "HBA" ], "RECV_devices": [ - "STAT/RCU2H/1" + "STAT/RCU2H/H0" ], "Control_to_RECV_mapping": [ "1", "24", @@ -203,15 +502,59 @@ " 0.00400627", " 0.60440575", "0.79666658" ] } - } - } - } - }, - "DigitalBeam": { - "STAT": { - "DigitalBeam": { - "STAT/DigitalBeam/HBA": { + }, + "STAT/AntennaField/LBA": { "properties": { + "Antenna_Type": [ + "LBA" + ], + "RECV_devices": [ + "STAT/RCU2L/L0", + "STAT/RCU2L/L1" + ], + "Control_to_RECV_mapping": [ + "1", "24", + "0", "-1", + "0", "-1", + "1", "25", + "1", "26" + ], + "Power_to_RECV_mapping": [ + "1", "27", + "0", "-1", + "0", "-1", + "1", "28", + "1", "29" + ], + "Antenna_to_SDP_Mapping": [ + "0", "0", + "0", "1", + "0", "2", + "0", "3", + "0", "4" + ], + "Antenna_Field_Reference_ETRS": [ + "3839371.416", "430339.901", "5057958.886" + ], + "Antenna_Reference_ETRS": [ + "3839371.416", "430339.901", "5057958.886", + "3839368.919", "430335.979", "5057961.1", + "3839365.645", "430339.299", "5057963.288", + "3839368.142", "430343.221", "5057961.074", + "3839374.094", "430299.513", "5057960.017" + ], + "HBAT_PQR_rotation_angles_deg": [ + "45.73", + "45.73", + "45.73", + "45.73", + "54.40" + ], + "PQR_to_ETRS_rotation_matrix": [ + "-0.11660087", "-0.79095632", "0.60065992", + " 0.99317077", "-0.09529842", "0.06730545", + " 0.00400627", " 0.60440575", "0.79666658" + ] } } } @@ -220,10 +563,10 @@ "RCU2H": { "STAT": { "RCU2H": { - "STAT/RCU2H/1": { + "STAT/RCU2H/H0": { "properties": { "OPC_Server_Name": [ - "10.99.0.101" + "10.99.2.101" ], "OPC_Server_Port": [ "4840" @@ -239,7 +582,7 @@ "RCU2L": { "STAT": { "RCU2L": { - "STAT/RCU2L/1": { + "STAT/RCU2L/L0": { "properties": { "OPC_Server_Name": [ "10.99.0.101" @@ -251,6 +594,19 @@ "5.0" ] } + }, + "STAT/RCU2L/L1": { + "properties": { + "OPC_Server_Name": [ + "10.99.1.101" + ], + "OPC_Server_Port": [ + "4840" + ], + "OPC_Time_Out": [ + "5.0" + ] + } } } } @@ -258,7 +614,7 @@ "SDP": { "STAT": { "SDP": { - "STAT/SDP/1": { + "STAT/SDP/LBA": { "properties": { "OPC_Server_Name": [ "10.99.0.250" @@ -292,10 +648,59 @@ "True", "True", "True", - "False", - "False", - "False", - "False", + "True", + "True", + "True", + "True", + "True", + "True", + "True", + "True", + "True", + "True", + "True", + "True" + ] + } + }, + "STAT/SDP/HBA": { + "properties": { + "OPC_Server_Name": [ + "10.99.0.250" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ], + "FPGA_sdp_info_station_id_RW_default": [ + "902", + "902", + "902", + "902", + "902", + "902", + "902", + "902", + "902", + "902", + "902", + "902", + "902", + "902", + "902", + "902" + ], + "TR_fpga_mask_RW_default": [ + "True", + "True", + "True", + "True", + "True", + "True", + "True", + "True", "False", "False", "False", @@ -313,8 +718,14 @@ "BST": { "STAT": { "BST": { - "STAT/BST/1": { + "STAT/BST/LBA": { "properties": { + "Statistics_Client_UDP_Port": [ + "5003" + ], + "Statistics_Client_TCP_Port": [ + "5103" + ], "OPC_Server_Name": [ "10.99.0.250" ], @@ -359,6 +770,97 @@ "10.99.250.250", "10.99.250.250", "10.99.250.250" + ], + "FPGA_bst_offload_hdr_udp_destination_port_RW_default": [ + "5003", + "5003", + "5003", + "5003", + "5003", + "5003", + "5003", + "5003", + "5003", + "5003", + "5003", + "5003", + "5003", + "5003", + "5003", + "5003" + ] + } + }, + "STAT/BST/HBA": { + "properties": { + "Statistics_Client_UDP_Port": [ + "5013" + ], + "Statistics_Client_TCP_Port": [ + "5113" + ], + "OPC_Server_Name": [ + "10.99.0.250" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ], + "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [ + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1" + ], + "FPGA_bst_offload_hdr_ip_destination_address_RW_default": [ + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250" + ], + "FPGA_bst_offload_hdr_udp_destination_port_RW_default": [ + "5013", + "5013", + "5013", + "5013", + "5013", + "5013", + "5013", + "5013", + "5013", + "5013", + "5013", + "5013", + "5013", + "5013", + "5013", + "5013" ] } } @@ -368,8 +870,14 @@ "SST": { "STAT": { "SST": { - "STAT/SST/1": { + "STAT/SST/LBA": { "properties": { + "Statistics_Client_UDP_Port": [ + "5001" + ], + "Statistics_Client_TCP_Port": [ + "5101" + ], "OPC_Server_Name": [ "10.99.0.250" ], @@ -414,6 +922,97 @@ "10.99.250.250", "10.99.250.250", "10.99.250.250" + ], + "FPGA_sst_offload_hdr_udp_destination_port_RW_default": [ + "5001", + "5001", + "5001", + "5001", + "5001", + "5001", + "5001", + "5001", + "5001", + "5001", + "5001", + "5001", + "5001", + "5001", + "5001", + "5001" + ] + } + }, + "STAT/SST/HBA": { + "properties": { + "Statistics_Client_UDP_Port": [ + "5011" + ], + "Statistics_Client_TCP_Port": [ + "5111" + ], + "OPC_Server_Name": [ + "10.99.0.250" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ], + "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [ + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1" + ], + "FPGA_sst_offload_hdr_ip_destination_address_RW_default": [ + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250" + ], + "FPGA_sst_offload_hdr_udp_destination_port_RW_default": [ + "5011", + "5011", + "5011", + "5011", + "5011", + "5011", + "5011", + "5011", + "5011", + "5011", + "5011", + "5011", + "5011", + "5011", + "5011", + "5011" ] } } @@ -423,8 +1022,14 @@ "XST": { "STAT": { "XST": { - "STAT/XST/1": { + "STAT/XST/LBA": { "properties": { + "Statistics_Client_UDP_Port": [ + "5002" + ], + "Statistics_Client_TCP_Port": [ + "5102" + ], "OPC_Server_Name": [ "10.99.0.250" ], @@ -469,6 +1074,97 @@ "10.99.250.250", "10.99.250.250", "10.99.250.250" + ], + "FPGA_xst_offload_hdr_udp_destination_port_RW_default": [ + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002" + ] + } + }, + "STAT/XST/HBA": { + "properties": { + "Statistics_Client_UDP_Port": [ + "5012" + ], + "Statistics_Client_TCP_Port": [ + "5112" + ], + "OPC_Server_Name": [ + "10.99.0.250" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ], + "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [ + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1", + "0c:c4:7a:c0:30:f1" + ], + "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [ + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250" + ], + "FPGA_xst_offload_hdr_udp_destination_port_RW_default": [ + "5012", + "5012", + "5012", + "5012", + "5012", + "5012", + "5012", + "5012", + "5012", + "5012", + "5012", + "5012", + "5012", + "5012", + "5012", + "5012" ] } } @@ -478,10 +1174,36 @@ "UNB2": { "STAT": { "UNB2": { - "STAT/UNB2/1": { + "STAT/UNB2/L0": { "properties": { "OPC_Server_Name": [ - "10.99.0.101" + "10.99.0.100" + ], + "OPC_Server_Port": [ + "4841" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + }, + "STAT/UNB2/L1": { + "properties": { + "OPC_Server_Name": [ + "10.99.1.100" + ], + "OPC_Server_Port": [ + "4841" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + }, + "STAT/UNB2/H0": { + "properties": { + "OPC_Server_Name": [ + "10.99.2.100" ], "OPC_Server_Port": [ "4841" diff --git a/README.md b/README.md index d5b7202a51a24d9c728481896d4b5fc4f7672728..737718c55f797d36c65b153f8a99d7bbf4d607c8 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,12 @@ Next change the version in the following places: # Release Notes -* 0.15.0 Split `recv` device into `rcu2h` and `rcu2l` and +* 0.16.2 Add Power_Parent and Parent_Children properties in LOFAR devices +* 0.16.1 AntennaField: Do not put device in FAULT if an attribute cannot be read/written. + AntennaField: Avoid archiving HBA-specific attributes for LBA fields. +* 0.16.0 Observation: Removed antenna mask from specification + DigitalBeam: Removed beamlet and antenna selection +* 0.15.0 Split `recv` device into `rcu2h` and `rcu2l` and split `recv-sim` translator into `rcu2h-sim` and `rcu2l-sim` * 0.14.0 Create async device base and make tilebeam and digitalbeam async device servers, allowing for cooperative multitasking and preventing issues with beamtracking. diff --git a/docker-compose/Makefile b/docker-compose/Makefile index 3fea6c7227bf1e2338b345ef24e5bc107aa37649..b74a77118c603b1d9e793529d5e5080f79b7ce15 100644 --- a/docker-compose/Makefile +++ b/docker-compose/Makefile @@ -223,6 +223,7 @@ bootstrap: pull build # first start, initialise from scratch $(MAKE) start dsconfig # boot up containers to load configurations sleep 5 # wait for dsconfig container to come up ../sbin/update_ConfigDb.sh ../CDB/LOFAR_ConfigDb.json # load default configuration + ../sbin/update_ConfigDb.sh ../CDB/hierarchies/power.json # load power hierarchy ../sbin/update_ConfigDb.sh ../CDB/stations/simulators_ConfigDb.json # by default, use simulators start: up ## start a service (usage: make start <servicename>) diff --git a/docker-compose/apsct-sim.yml b/docker-compose/apsct-sim.yml index ae3c6beabf23bf229a80ddb738ddfca3f72ed04e..d6ef5b9a579976c0952020030a829bef637f087f 100644 --- a/docker-compose/apsct-sim.yml +++ b/docker-compose/apsct-sim.yml @@ -15,6 +15,7 @@ services: args: - LOCAL_DOCKER_REGISTRY_HOST=${LOCAL_DOCKER_REGISTRY_HOST} - LOCAL_DOCKER_REGISTRY_LOFAR=${LOCAL_DOCKER_REGISTRY_LOFAR} + hostname: apsct-sim container_name: apsct-sim logging: driver: "json-file" diff --git a/docker-compose/apspu-sim.yml b/docker-compose/apspu-sim.yml index d0f3dec1813ba2fe3112d5bfc94e89b2e9f59457..ebfddbaa8bb2963441fe93f239256bc8bb98c88e 100644 --- a/docker-compose/apspu-sim.yml +++ b/docker-compose/apspu-sim.yml @@ -15,6 +15,7 @@ services: args: - LOCAL_DOCKER_REGISTRY_HOST=${LOCAL_DOCKER_REGISTRY_HOST} - LOCAL_DOCKER_REGISTRY_LOFAR=${LOCAL_DOCKER_REGISTRY_LOFAR} + hostname: apspu-sim container_name: apspu-sim logging: driver: "json-file" diff --git a/docker-compose/ccd-sim.yml b/docker-compose/ccd-sim.yml index 0ae852469e56e9048b087b824f1ba422a029e17a..1580b222ec4be3f86de7d3df057efcb3a01e6ce6 100644 --- a/docker-compose/ccd-sim.yml +++ b/docker-compose/ccd-sim.yml @@ -15,6 +15,7 @@ services: args: - LOCAL_DOCKER_REGISTRY_HOST=${LOCAL_DOCKER_REGISTRY_HOST} - LOCAL_DOCKER_REGISTRY_LOFAR=${LOCAL_DOCKER_REGISTRY_LOFAR} + hostname: ccd-sim container_name: ccd-sim logging: driver: "json-file" diff --git a/docker-compose/device-antennafield.yml b/docker-compose/device-antennafield.yml index 3dca9bbd9269986a98d648ca0e3287fe50b2edfd..5272302432c4ef5cc69aa2c7236e567234198b46 100644 --- a/docker-compose/device-antennafield.yml +++ b/docker-compose/device-antennafield.yml @@ -18,6 +18,7 @@ version: '2.1' services: device-antennafield: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-antennafield container_name: device-antennafield logging: driver: "json-file" diff --git a/docker-compose/device-apsct.yml b/docker-compose/device-apsct.yml index 4580fbbb19914b3195bbc3734c85bf04880913d2..57a90c10c843d56219d45610b6668c8df695787f 100644 --- a/docker-compose/device-apsct.yml +++ b/docker-compose/device-apsct.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-apsct: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-apsct container_name: device-apsct logging: driver: "json-file" diff --git a/docker-compose/device-apspu.yml b/docker-compose/device-apspu.yml index a3edc93d0e482db6f394b8ed1ab22478f134ff7a..5e2de1a66a65eafab4443f04415dc5aa77b598f9 100644 --- a/docker-compose/device-apspu.yml +++ b/docker-compose/device-apspu.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-apspu: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-apspu container_name: device-apspu logging: driver: "json-file" diff --git a/docker-compose/device-beamlet.yml b/docker-compose/device-beamlet.yml index 3b5b47947acfdd5162234fec36986c4b10c4a367..1cf7dc5a2f265d45be7700e2c368f7a83a67b53c 100644 --- a/docker-compose/device-beamlet.yml +++ b/docker-compose/device-beamlet.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-beamlet: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-beamlet container_name: device-beamlet logging: driver: "json-file" diff --git a/docker-compose/device-boot.yml b/docker-compose/device-boot.yml index efc4db80e3f63a586845f9e213ea3d2344554d88..8472267963ef41407cbca5e10a297c304eba7933 100644 --- a/docker-compose/device-boot.yml +++ b/docker-compose/device-boot.yml @@ -16,6 +16,7 @@ version: '2.1' services: device-boot: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-boot container_name: device-boot logging: driver: "json-file" diff --git a/docker-compose/device-bst.yml b/docker-compose/device-bst.yml index 9c55b68fb2859f5dcf461e8b2ed547a184d8b321..69d71ff97f2006d943fa535196b760c4c86f0787 100644 --- a/docker-compose/device-bst.yml +++ b/docker-compose/device-bst.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-bst: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-bst container_name: device-bst logging: driver: "json-file" @@ -27,8 +28,10 @@ services: - control - data ports: - - "5003:5003/udp" # port to receive SST UDP packets on + - "5003:5003/udp" # port to receive SST UDP packets on (first antennafield) - "5103:5103/tcp" # port to emit SST TCP packets on + - "5013:5013/udp" # port to receive SST UDP packets on (second antennafield) + - "5113:5113/tcp" # port to emit SST TCP packets on - "5717:5717" # unique port for this DS - "5817:5817" # ZeroMQ event port - "5917:5917" # ZeroMQ heartbeat port diff --git a/docker-compose/device-calibration.yml b/docker-compose/device-calibration.yml index f973c92a38354c6e696d0bd4b71605d1b29d3b45..a024ab8a2a0e9207e3c9ea294cca6a667c705bf0 100644 --- a/docker-compose/device-calibration.yml +++ b/docker-compose/device-calibration.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-calibration: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-calibration container_name: device-calibration logging: driver: "json-file" diff --git a/docker-compose/device-ccd.yml b/docker-compose/device-ccd.yml index ba83540bbb45230905278e210577d4284f7bf1dd..4e2810ddaae0b4e599a4ea17520f0e2973a89a3a 100644 --- a/docker-compose/device-ccd.yml +++ b/docker-compose/device-ccd.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-ccd: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-ccd container_name: device-ccd logging: driver: "json-file" diff --git a/docker-compose/device-configuration.yml b/docker-compose/device-configuration.yml index caef8603f83b726ab7b3ee15ffe93b784bd83eb7..346a999a5716f48d5134fa9b05339c70f20248ef 100644 --- a/docker-compose/device-configuration.yml +++ b/docker-compose/device-configuration.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-configuration: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-configuration container_name: device-configuration logging: driver: "json-file" diff --git a/docker-compose/device-digitalbeam.yml b/docker-compose/device-digitalbeam.yml index 168924db2e5a6af700a2810f72e21112bcfddd12..2ad5c7d24050b3b8f5021df8f627c3fcd38178aa 100644 --- a/docker-compose/device-digitalbeam.yml +++ b/docker-compose/device-digitalbeam.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-digitalbeam: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-digitalbeam container_name: device-digitalbeam logging: driver: "json-file" diff --git a/docker-compose/device-docker.yml b/docker-compose/device-docker.yml index 004bea2eb1b014611ed7d4ad224aa99c809687e9..c31130996c9e92291f9b9a521b5fb6ded181184d 100644 --- a/docker-compose/device-docker.yml +++ b/docker-compose/device-docker.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-docker: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-docker container_name: device-docker logging: driver: "json-file" diff --git a/docker-compose/device-observation-control.yml b/docker-compose/device-observation-control.yml index 9bd0a7cfa66e200a86634ed6824e02deaa22d383..c4a3e4bf0bb7bc5861c30335dbe7acf49e86266f 100644 --- a/docker-compose/device-observation-control.yml +++ b/docker-compose/device-observation-control.yml @@ -16,6 +16,7 @@ version: '2.1' services: device-observation-control: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-observation-control container_name: device-observation-control logging: driver: "json-file" diff --git a/docker-compose/device-observation.yml b/docker-compose/device-observation.yml index 6d29f38875404ab8fb23318777c60527f3b532c6..3d4b24168c96306c67bd4a0c7b91485b8bf77794 100644 --- a/docker-compose/device-observation.yml +++ b/docker-compose/device-observation.yml @@ -15,6 +15,7 @@ version: '2.1' services: device-observation: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-observation container_name: device-observation logging: driver: "json-file" diff --git a/docker-compose/device-pcon.yml b/docker-compose/device-pcon.yml index bca66d499241213ba78f900d9a74c7e8a5d36192..5b900769107dc097af35c0efdd49fbf814e417a2 100644 --- a/docker-compose/device-pcon.yml +++ b/docker-compose/device-pcon.yml @@ -12,6 +12,7 @@ volumes: services: device-pcon: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-pcon container_name: device-pcon logging: driver: "json-file" diff --git a/docker-compose/device-psoc.yml b/docker-compose/device-psoc.yml index f85b0a1891eccd3d74a8cad784acb5760d18e558..ea3ef27d9af6ae2475601ab2064c0e1752da8d1b 100644 --- a/docker-compose/device-psoc.yml +++ b/docker-compose/device-psoc.yml @@ -12,6 +12,7 @@ volumes: services: device-psoc: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-psoc container_name: device-psoc logging: driver: "json-file" diff --git a/docker-compose/device-rcu2h.yml b/docker-compose/device-rcu2h.yml index 4ab84dd9de43be83c7960af8fa4b312bd5dac092..48594945a5a297940f2347cb8e894bf695312362 100644 --- a/docker-compose/device-rcu2h.yml +++ b/docker-compose/device-rcu2h.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-rcu2h: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-rcu2h container_name: device-rcu2h logging: driver: "json-file" diff --git a/docker-compose/device-rcu2l.yml b/docker-compose/device-rcu2l.yml index 884f4d5bfbc166680918eeb3d4d6ad82e754f4bf..a499d8b7c81479487b3fa82042d6c3576aba9092 100644 --- a/docker-compose/device-rcu2l.yml +++ b/docker-compose/device-rcu2l.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-rcu2l: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-rcu2l container_name: device-rcu2l logging: driver: "json-file" diff --git a/docker-compose/device-sdp.yml b/docker-compose/device-sdp.yml index a3f83c9e59a104abaedd642b1d67d21a412d98a3..f9e45c368cb5998926d80f8fe27183a5091d7fa8 100644 --- a/docker-compose/device-sdp.yml +++ b/docker-compose/device-sdp.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-sdp: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-sdp container_name: device-sdp logging: driver: "json-file" diff --git a/docker-compose/device-sst.yml b/docker-compose/device-sst.yml index 7f5f81119a41f14f11f7c99b24d07beb2b46326f..1e430f5bfe2d7ec15886b63f4f288120e5a98431 100644 --- a/docker-compose/device-sst.yml +++ b/docker-compose/device-sst.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-sst: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-sst container_name: device-sst logging: driver: "json-file" @@ -27,8 +28,10 @@ services: - control - data ports: - - "5001:5001/udp" # port to receive SST UDP packets on + - "5001:5001/udp" # port to receive SST UDP packets on (first antennafield) - "5101:5101/tcp" # port to emit SST TCP packets on + - "5011:5011/udp" # port to receive SST UDP packets on (second antennafield) + - "5111:5111/tcp" # port to emit SST TCP packets on - "5702:5702" # unique port for this DS - "5802:5802" # ZeroMQ event port - "5902:5902" # ZeroMQ heartbeat port diff --git a/docker-compose/device-station-manager.yml b/docker-compose/device-station-manager.yml index 68c525cc71c3e05f7b82fabd796590546b03f773..24c124256b74238495b8288d305dcef44c931f9a 100644 --- a/docker-compose/device-station-manager.yml +++ b/docker-compose/device-station-manager.yml @@ -12,6 +12,7 @@ volumes: services: device-station-manager: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-station-manager container_name: device-station-manager logging: driver: "json-file" diff --git a/docker-compose/device-temperature-manager.yml b/docker-compose/device-temperature-manager.yml index 386893d5b423fc918c4b00e81af6cf5ca9de10c2..9bb1a4589ff5effb6ebe2cfae19cbc10b117d273 100644 --- a/docker-compose/device-temperature-manager.yml +++ b/docker-compose/device-temperature-manager.yml @@ -12,6 +12,7 @@ volumes: services: device-temperature-manager: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-temperature-manager container_name: device-temperature-manager logging: driver: "json-file" diff --git a/docker-compose/device-tilebeam.yml b/docker-compose/device-tilebeam.yml index df26f30abf7d292c3d3db54af2689f9cf27ade16..3bd65a9ca2a0eb0db3a0b2714199c0d5160fe233 100644 --- a/docker-compose/device-tilebeam.yml +++ b/docker-compose/device-tilebeam.yml @@ -12,6 +12,7 @@ volumes: services: device-tilebeam: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-tilebeam container_name: device-tilebeam logging: driver: "json-file" diff --git a/docker-compose/device-unb2.yml b/docker-compose/device-unb2.yml index 8dc893bb0fbc91ae04da2aa43ec572c14c4add0b..ed5a4309808db5edfbab6c063bae3c2abdc5a204 100644 --- a/docker-compose/device-unb2.yml +++ b/docker-compose/device-unb2.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-unb2: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-unb2 container_name: device-unb2 logging: driver: "json-file" diff --git a/docker-compose/device-xst.yml b/docker-compose/device-xst.yml index ea7d674d6e1337da18d3c3a25d08893dfb603ade..e064d4eca68c008efd91fe504ea81236e557cecf 100644 --- a/docker-compose/device-xst.yml +++ b/docker-compose/device-xst.yml @@ -17,6 +17,7 @@ version: '2.1' services: device-xst: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base + hostname: device-xst container_name: device-xst logging: driver: "json-file" @@ -27,8 +28,10 @@ services: - control - data ports: - - "5002:5002/udp" # port to receive XST UDP packets on + - "5002:5002/udp" # port to receive XST UDP packets on (first antennafield) - "5102:5102/tcp" # port to emit XST TCP packets on + - "5012:5012/udp" # port to receive XST UDP packets on (second antennafield) + - "5112:5112/tcp" # port to emit XST TCP packets on - "5706:5706" # unique port for this DS - "5806:5806" # ZeroMQ event port - "5906:5906" # ZeroMQ heartbeat port diff --git a/docker-compose/grafana.yml b/docker-compose/grafana.yml index ae60f2800a6a5603d507c0161fae34cdcac2b96d..64b13e0bd3c752746d46ac9d002350622d92f20b 100644 --- a/docker-compose/grafana.yml +++ b/docker-compose/grafana.yml @@ -18,6 +18,7 @@ services: image: grafana build: context: grafana + hostname: grafana container_name: grafana networks: - control diff --git a/docker-compose/http-json-schemas.yml b/docker-compose/http-json-schemas.yml index c8661f3f96a8be7b50c5d55c20d567c7c10f1897..314b2a47f67b93986fc70ce78ed120c4b4aa9502 100644 --- a/docker-compose/http-json-schemas.yml +++ b/docker-compose/http-json-schemas.yml @@ -14,12 +14,10 @@ services: http-json-schemas: build: context: http-json-schemas + hostname: http-json-schemas container_name: http-json-schemas networks: - control - # set the hostname, otherwise duplicate device registrations result every - # time the hostname changes as the container is restarted. - hostname: http-json-schemas environment: - NGINX_HOST=http-json-schemas - NGINX_PORT=80 diff --git a/docker-compose/http-json-schemas/definitions/observation-settings.json b/docker-compose/http-json-schemas/definitions/observation-settings.json index 97c778620b6e1a5ad38643ab0dc56ee34a563100..7e0aef69cbdb5f2b8bab603d8aca63623a32ba56 100644 --- a/docker-compose/http-json-schemas/definitions/observation-settings.json +++ b/docker-compose/http-json-schemas/definitions/observation-settings.json @@ -5,7 +5,6 @@ "observation_id", "stop_time", "antenna_set", - "antenna_mask", "filter", "SAPs" ], @@ -30,14 +29,6 @@ "SPARSE_ODD" ] }, - "antenna_mask": { - "type": "array", - "uniqueItems": true, - "minItems": 1, - "items": { - "type": "number" - } - }, "filter": { "type": "string", "enum": [ diff --git a/docker-compose/integration-test.yml b/docker-compose/integration-test.yml index 1d771d6d4ccc5a3c558f6b088a6cc29795fc04b5..0908c50d32f0cd3d9b091555c95e892b4209730b 100644 --- a/docker-compose/integration-test.yml +++ b/docker-compose/integration-test.yml @@ -15,6 +15,7 @@ services: dockerfile: ci-runner/Dockerfile args: SOURCE_IMAGE: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-itango:${TANGO_ITANGO_VERSION} + hostname: integration-test container_name: integration-test networks: - control diff --git a/docker-compose/itango.yml b/docker-compose/itango.yml index 6f6f7aeb56e1f25a4bec1fadc2765e4cefd41d18..dde5295ea2b458fae1379f2ba7aa90ecb52da5fc 100644 --- a/docker-compose/itango.yml +++ b/docker-compose/itango.yml @@ -20,6 +20,7 @@ services: context: itango args: SOURCE_IMAGE: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-itango:${TANGO_ITANGO_VERSION} + hostname: itango container_name: itango logging: driver: "json-file" diff --git a/docker-compose/jupyter-lab.yml b/docker-compose/jupyter-lab.yml index c2136156a665cbc1b77a5c1ffa57a116105dd369..11192b32fa0029ecff265808be331072c6abeacb 100644 --- a/docker-compose/jupyter-lab.yml +++ b/docker-compose/jupyter-lab.yml @@ -19,6 +19,7 @@ services: args: CONTAINER_EXECUTION_UID: ${CONTAINER_EXECUTION_UID} SOURCE_IMAGE: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-itango:${TANGO_ITANGO_VERSION} + hostname: jupyter-lab container_name: jupyter-lab logging: driver: "json-file" diff --git a/docker-compose/jupyterlab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py b/docker-compose/jupyterlab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py index 3b38808fe1e7c31065c5cd245feb163fe4461e0b..065d12a2d7c1527914578e04352bbb148cccda9d 100644 --- a/docker-compose/jupyterlab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py +++ b/docker-compose/jupyterlab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py @@ -1,25 +1,90 @@ -# Create shortcuts for our devices -apsct = DeviceProxy("STAT/APSCT/1") -ccd = DeviceProxy("STAT/CCD/1") -apspu = DeviceProxy("STAT/APSPU/1") -rcu2h = DeviceProxy("STAT/RCU2H/1") -rcu2l = DeviceProxy("STAT/RCU2L/1") -sdp = DeviceProxy("STAT/SDP/1") -bst = DeviceProxy("STAT/BST/1") -sst = DeviceProxy("STAT/SST/1") -xst = DeviceProxy("STAT/XST/1") -unb2 = DeviceProxy("STAT/UNB2/1") -stationmanager = DeviceProxy("STAT/StationManager/1") -boot = DeviceProxy("STAT/Boot/1") -tilebeam = DeviceProxy("STAT/TileBeam/HBA") -pcon = DeviceProxy("STAT/PCON/1") -psoc = DeviceProxy("STAT/PSOC/1") -beamlet = DeviceProxy("STAT/Beamlet/1") -digitalbeam = DeviceProxy("STAT/DigitalBeam/HBA") -antennafield = DeviceProxy("STAT/AntennaField/HBA") -docker = DeviceProxy("STAT/Docker/1") -temperaturemanager = DeviceProxy("STAT/TemperatureManager/1") -configuration = DeviceProxy("STAT/Configuration/1") +# Create shortcuts for our devices, if they exist + + +def OptionalDeviceProxy(device_name: str): + """Return a DeviceProxy for the given device, or None.""" + try: + return DeviceProxy(device_name) + except DevFailed: + # device is not in database, or otherwise not reachable + return None + + +apsct_l0 = OptionalDeviceProxy("STAT/APSCT/L0") +apsct_l1 = OptionalDeviceProxy("STAT/APSCT/L1") +apsct_h0 = OptionalDeviceProxy("STAT/APSCT/H0") +apscts = [apsct_l0, apsct_l1, apsct_h0] + +apspu_l0 = OptionalDeviceProxy("STAT/APSPU/L0") +apspu_l1 = OptionalDeviceProxy("STAT/APSPU/L1") +apspu_h0 = OptionalDeviceProxy("STAT/APSPU/H0") +apspus = [apspu_l0, apspu_l1, apspu_h0] + +recvl_l0 = OptionalDeviceProxy("STAT/RCU2L/L0") +recvl_l1 = OptionalDeviceProxy("STAT/RCU2L/L1") +recvh_h0 = OptionalDeviceProxy("STAT/RCU2H/H0") +recvs = [recvl_l0, recvl_l1, recvh_h0] + +unb2_l0 = OptionalDeviceProxy("STAT/UNB2/L0") +unb2_l1 = OptionalDeviceProxy("STAT/UNB2/L1") +unb2_h0 = OptionalDeviceProxy("STAT/UNB2/H0") +unb2s = [unb2_l0, unb2_l1, unb2_h0] + +sdp_l = OptionalDeviceProxy("STAT/SDP/LBA") +bst_l = OptionalDeviceProxy("STAT/BST/LBA") +sst_l = OptionalDeviceProxy("STAT/SST/LBA") +xst_l = OptionalDeviceProxy("STAT/XST/LBA") +beamlet_l = OptionalDeviceProxy("STAT/Beamlet/LBA") +digitalbeam_l = OptionalDeviceProxy("STAT/DigitalBeam/LBA") +antennafield_l = OptionalDeviceProxy("STAT/AntennaField/LBA") + +sdp_h = OptionalDeviceProxy("STAT/SDP/HBA") +bst_h = OptionalDeviceProxy("STAT/BST/HBA") +sst_h = OptionalDeviceProxy("STAT/SST/HBA") +xst_h = OptionalDeviceProxy("STAT/XST/HBA") +beamlet_h = OptionalDeviceProxy("STAT/Beamlet/HBA") +digitalbeam_h = OptionalDeviceProxy("STAT/DigitalBeam/HBA") +tilebeam_h = OptionalDeviceProxy("STAT/TileBeam/HBA") +antennafield_h = OptionalDeviceProxy("STAT/AntennaField/HBA") + +stationmanager = OptionalDeviceProxy("STAT/StationManager/1") +boot = OptionalDeviceProxy("STAT/Boot/1") +ccd = OptionalDeviceProxy("STAT/CCD/1") +pcon = OptionalDeviceProxy("STAT/PCON/1") +psoc = OptionalDeviceProxy("STAT/PSOC/1") +docker = OptionalDeviceProxy("STAT/Docker/1") +temperaturemanager = OptionalDeviceProxy("STAT/TemperatureManager/1") +configuration = OptionalDeviceProxy("STAT/Configuration/1") # Put them in a list in case one wants to iterate -devices = [apsct, ccd, apspu, rcu2h, rcu2l, sdp, bst, sst, xst, unb2, boot, stationmanager, tilebeam, beamlet, digitalbeam, antennafield, temperaturemanager, docker, pcon, psoc, configuration] +devices = ( + [ + stationmanager, + boot, + ccd, + pcon, + psoc, + docker, + temperaturemanager, + configuration, + sdp_l, + bst_l, + sst_l, + xst_l, + beamlet_l, + digitalbeam_l, + antennafield_l, + sdp_h, + bst_h, + sst_h, + xst_h, + beamlet_h, + digitalbeam_h, + tilebeam_h, + antennafield_h, + ] + + apscts + + apspus + + recvs + + unb2s +) diff --git a/docker-compose/jupyterlab/requirements.txt b/docker-compose/jupyterlab/requirements.txt index 1b730a0bd8ac8ecdbdd9be062b0ba6ea2593049b..9031da2e0773178b064809b4fc878d5424b41fae 100644 --- a/docker-compose/jupyterlab/requirements.txt +++ b/docker-compose/jupyterlab/requirements.txt @@ -8,6 +8,7 @@ matplotlib jupyterplot nbconvert notebook-as-pdf +PyPDF2==2.12.1 # until https://github.com/betatim/notebook-as-pdf/issues/40 hits a notebook-as-pdf release python-logstash-async PyMySQL[rsa] psycopg2-binary >= 2.9.2 #LGPL diff --git a/docker-compose/lofar-device-base.yml b/docker-compose/lofar-device-base.yml index 725fc10ab82a2944d8b633efc0d32099c9ac0f16..777bb167bfb69d42ffa8f332d306d9e7af12e766 100644 --- a/docker-compose/lofar-device-base.yml +++ b/docker-compose/lofar-device-base.yml @@ -23,6 +23,7 @@ services: dockerfile: lofar-device-base/Dockerfile args: SOURCE_IMAGE: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-itango:${TANGO_ITANGO_VERSION} + hostname: lofar-device-base container_name: lofar-device-base # These parameters are just visual queues, you have to define them again # in derived docker-compose files! diff --git a/docker-compose/logstash.yml b/docker-compose/logstash.yml index 283ef0fb0a76cae6eab432725fe26739f1032210..9a20d061622b1bc78ece7858c8b63193164b4a66 100644 --- a/docker-compose/logstash.yml +++ b/docker-compose/logstash.yml @@ -14,6 +14,7 @@ services: context: logstash args: SOURCE_IMAGE: grafana/logstash-output-loki:main + hostname: logstash container_name: logstash logging: driver: "json-file" @@ -35,3 +36,17 @@ services: - "5959:5959" # logstash tcp json input - "9600:9600" restart: unless-stopped + + logstash-exporter: + image: sequra/logstash_exporter + hostname: logstash-exporter + container_name: logstash-exporter + logging: + driver: "json-file" + options: + max-size: "100m" + max-file: "10" + networks: + - control + command: --logstash.endpoint="http://logstash:9600" + restart: unless-stopped diff --git a/docker-compose/logstash/logstash.yml b/docker-compose/logstash/logstash.yml index 5f80650fe6fc635570fd9f7e4888da17eddf4e70..a5c9f331919bf8ff4f6f16aff14668cd48c9c345 100644 --- a/docker-compose/logstash/logstash.yml +++ b/docker-compose/logstash/logstash.yml @@ -1,2 +1,4 @@ http.host: "0.0.0.0" #xpack.monitoring.elasticsearch.hosts: [ "http://loki:3100" ] + +pipeline.ecs_compatibility: disabled diff --git a/docker-compose/logstash/loki.conf b/docker-compose/logstash/loki.conf index b0b22e26996f82f5dcdb9561183090abb131dbad..177ea72c8237826b40ef94323382c76f5c92781a 100644 --- a/docker-compose/logstash/loki.conf +++ b/docker-compose/logstash/loki.conf @@ -1,5 +1,8 @@ +# For syntax, see https://www.elastic.co/guide/en/logstash/current/filter-plugins.html + input { beats { + id => "input_beats" port => 5044 # TODO (L2SS-748) add SSL encryption } @@ -7,12 +10,14 @@ input { input { syslog { + id => "input_syslog" port => 1514 } } input { tcp { + id => "input_tcp_json" port => 5959 codec => json } @@ -21,12 +26,14 @@ input { filter { if [type] == "syslog" { grok { + id => "grok_syslog" match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" } add_field => [ "received_at", "%{@timestamp}" ] add_field => [ "received_from", "%{host}" ] } - syslog_pri { } + syslog_pri { id => "syslog_pri" } date { + id => "date_syslog" match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ] } } @@ -34,48 +41,35 @@ filter { filter { if [program] == "grafana" { - kv { } + kv { id => "kv_grafana" } mutate { + id => "mutate_grafana" rename => { "t" => "timestamp" "lvl" => "level" - "msg" => "message" } uppercase => [ "level" ] } - date { - match => [ "timestamp", "ISO8601" ] - } - } -} -filter { - if [program] == "prometheus" { - kv { } - mutate { - rename => { - "ts" => "timestamp" - "msg" => "message" - } - uppercase => [ "level" ] - } date { - match => [ "timestamp", "ISO8601" ] + id => "date_grafana" + match => [ "timestamp", "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z'", "ISO8601" ] } } } filter { if [program] == "prometheus" { - kv { } + kv { id => "kv_prometheus" } mutate { + id => "mutate_prometheus" rename => { "ts" => "timestamp" - "msg" => "message" } uppercase => [ "level" ] } date { + id => "date_prometheus" match => [ "timestamp", "ISO8601" ] } } @@ -84,12 +78,14 @@ filter { filter { if [program] == "tango-rest" { grok { + id => "grok_tango-rest" match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{WORD:level} %{GREEDYDATA:message}" } - "overwrite" => [ "timestamp", "level", "message" ] + overwrite => [ "timestamp", "level", "message" ] } date { + id => "date_tango-rest" match => [ "timestamp", "YYYY-MM-dd HH:mm:ss,SSS" ] timezone => "UTC" } @@ -97,17 +93,10 @@ filter { } filter { - # mark our tangodb instances - grok { - match => { - "program" => ["tangodb" ] - } - add_tag => [ "tangodb" ] - } - # parse tangodb output - if "tangodb" in [tags] { + if [program] == "tangodb" { grok { + id => "grok_tangodb" match => { "message" => [ "%{TIMESTAMP_ISO8601:timestamp} .%{WORD:level}. %{GREEDYDATA:message}", @@ -117,21 +106,114 @@ filter { "overwrite" => [ "timestamp", "level", "message" ] } mutate { + id => "mutate_tangodb" gsub => [ "level", "Note", "Info" ] uppercase => [ "level" ] } date { + id => "date_tangodb" match => [ "timestamp", "YYYY-MM-dd HH:mm:ssZZ", "YYYY-MM-dd HH:mm:ss", "YYYY-MM-dd H:mm:ss" ] timezone => "UTC" } } } +filter { + if [type] == "python-logstash" { + # don't archive debug messages + if [level] == "DEBUG" { + drop { id => "drop_debug_message" } + } + + # strip path from program + grok { + id => "grok_python-logstash-removepath-program" + match => { + "program" => "(?<program>[^/]+)$" + } + + overwrite => [ "program" ] + tag_on_failure => [] # be quiet if there are no matches + } + + # strip path from source reference + grok { + id => "grok_python-logstash-removepath-source" + match => { + "[extra][path]" => "(?<[extra][path]>[^/]+)$" + } + + overwrite => [ "[extra][path]" ] + tag_on_failure => [] # be quiet if there are no matches + } + + # add source reference to each log entry + mutate { + id => "mutate_python-logstash-sourceref" + replace => { "message" => "%{message} [%{[extra][func_name]}() at %{[extra][path]}:%{[extra][line]}]" } + } + + # promote some fields out of extra, if present + if [extra][device] { + mutate { + id => "mutate_python-logstash-device" + add_field => { + "device" => "%{[extra][device]}" + } + } + } + + if [extra][stack_trace] { + mutate { + id => "mutate_python-logstash-stacktrace" + replace => { "message" => "%{message} %{[extra][stack_trace]}" } + } + } + } +} + +# Throttle spam to max 1000/min per program/device. +# See https://www.elastic.co/guide/en/logstash/current/plugins-filters-throttle.html +filter { + throttle { + id => "throttler" + before_count => -1 + after_count => 1000 + period => 60 + max_age => 180 + key => "%{host}-%{program}-%{device}" + add_tag => "throttled" + } + + if "throttled" in [tags] { + drop { id => "drop_throttled" } + } +} + output { loki { + id => "output_loki" url => "http://loki:3100/loki/api/v1/push" + message_field => "message" + + # Every unique combination of field values creates a separate stream in loki. + # This overloads loki, so only send fields that are key and bank on messages + # being regex-ed when querying instead. + # + # See https://grafana.com/blog/2020/08/27/the-concise-guide-to-labels-in-loki/ + # + # So avoid putting in fields that vary, such as: + # * (parts of) timestamps, + # * source file references (file, line, etc). + # Instead, f.e. put those in the message field with a replace filter. + # + # To inspect loki's streams, look at the content of /loki/chunks in the loki container: + # docker exec -it loki ls /loki/chunks + include_fields => [ "host", "program", "level", "device", "tags" ] } -} + # enable this to see all logs on logstash's stdout. to view, run: docker logs logstash + # stdout {} +} diff --git a/docker-compose/loki.yml b/docker-compose/loki.yml index fa114fcdecf72042f97100af47f90673fb2a57ec..58a10e5e07a016a3065ea63380503f68306131e1 100644 --- a/docker-compose/loki.yml +++ b/docker-compose/loki.yml @@ -8,9 +8,14 @@ version: "2.1" +volumes: + loki-data: {} + services: loki: - image: grafana/loki:2.6.0 + build: + context: loki + hostname: loki container_name: loki logging: driver: "json-file" @@ -19,6 +24,8 @@ services: max-file: "10" networks: - control + volumes: + - loki-data:/loki ports: - "3100:3100" command: -config.file=/etc/loki/local-config.yaml diff --git a/docker-compose/loki/Dockerfile b/docker-compose/loki/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..466a31876e3fed1a021d9b928a19edb534f9f3e0 --- /dev/null +++ b/docker-compose/loki/Dockerfile @@ -0,0 +1,2 @@ +FROM grafana/loki:2.6.0 +COPY local-config.yaml /etc/loki/local-config.yaml diff --git a/docker-compose/loki/local-config.yaml b/docker-compose/loki/local-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..acac70bbd45b5159eecdfa64a1d6e744fc515a58 --- /dev/null +++ b/docker-compose/loki/local-config.yaml @@ -0,0 +1,41 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + +common: + path_prefix: /loki + storage: + filesystem: + chunks_directory: /loki/chunks + rules_directory: /loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +schema_config: + configs: + - from: 2020-10-24 + store: boltdb-shipper + object_store: filesystem + schema: v11 + index: + prefix: index_ + period: 24h + +ruler: + alertmanager_url: http://localhost:9093 + +# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration +# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ +# +# Statistics help us better understand how Loki is used, and they show us performance +# levels for most users. This helps us prioritize features and documentation. +# For more information on what's sent, look at +# https://github.com/grafana/loki/blob/main/pkg/usagestats/stats.go +# Refer to the buildReport method to see what goes into a report. +# +# If you would like to disable reporting, uncomment the following lines: +analytics: + reporting_enabled: false diff --git a/docker-compose/object-storage.yml b/docker-compose/object-storage.yml index 5ba417b415080e287500149896f727216a66b95c..feebcee6b94deebecbec658ca6af7aaddad7af60 100644 --- a/docker-compose/object-storage.yml +++ b/docker-compose/object-storage.yml @@ -11,6 +11,7 @@ version: '2.1' services: object-storage: image: minio/minio + hostname: object-storage container_name: object-storage logging: driver: "json-file" diff --git a/docker-compose/prometheus-node-exporter.yml b/docker-compose/prometheus-node-exporter.yml index 84fffd2949534aa51bb9adda6dfd5a7d813ad740..f61e4b8450928d5c9b55c7d368b17dd8d818ab45 100644 --- a/docker-compose/prometheus-node-exporter.yml +++ b/docker-compose/prometheus-node-exporter.yml @@ -11,6 +11,7 @@ version: '2.1' services: prometheus-node-exporter: image: prom/node-exporter + hostname: prometheus-node-exporter container_name: prometheus-node-exporter network_mode: host # run on the host to be able to access host network statistics logging: diff --git a/docker-compose/prometheus.yml b/docker-compose/prometheus.yml index f398a9d2a3ad8322a0e6de77073b8be1519b8081..4ba6b702991ef89efab3edc489cfc640c5fb22b4 100644 --- a/docker-compose/prometheus.yml +++ b/docker-compose/prometheus.yml @@ -17,6 +17,7 @@ services: image: prometheus build: context: prometheus + hostname: prometheus container_name: prometheus networks: - control diff --git a/docker-compose/prometheus/prometheus.yml b/docker-compose/prometheus/prometheus.yml index ea073cbf92c0a5c070ca94dea1ccac03d48ff225..987eaf6225be15b7d01590720c9098ccfc90b415 100644 --- a/docker-compose/prometheus/prometheus.yml +++ b/docker-compose/prometheus/prometheus.yml @@ -32,3 +32,13 @@ scrape_configs: - targets: ["grafana:3000"] labels: "host": "localhost" + - job_name: logstash + static_configs: + - targets: ["logstash-exporter:9198"] + labels: + "host": "localhost" + - job_name: loki + static_configs: + - targets: ["loki:3100"] + labels: + "host": "localhost" diff --git a/docker-compose/rcu2h-sim.yml b/docker-compose/rcu2h-sim.yml index e61322aceb083e1aefa5fe02e98a166ad5425eb3..9e705abcceabebdb0f3e069fb3b03c73f10c3eb2 100644 --- a/docker-compose/rcu2h-sim.yml +++ b/docker-compose/rcu2h-sim.yml @@ -15,6 +15,7 @@ services: args: - LOCAL_DOCKER_REGISTRY_HOST=${LOCAL_DOCKER_REGISTRY_HOST} - LOCAL_DOCKER_REGISTRY_LOFAR=${LOCAL_DOCKER_REGISTRY_LOFAR} + hostname: rcu2h-sim container_name: rcu2h-sim logging: driver: "json-file" diff --git a/docker-compose/rcu2l-sim.yml b/docker-compose/rcu2l-sim.yml index 4e6e533768c90d74bc5a69de9bf917a922a34773..6c2092019898f79b740a4a84507724294d923e22 100644 --- a/docker-compose/rcu2l-sim.yml +++ b/docker-compose/rcu2l-sim.yml @@ -15,6 +15,7 @@ services: args: - LOCAL_DOCKER_REGISTRY_HOST=${LOCAL_DOCKER_REGISTRY_HOST} - LOCAL_DOCKER_REGISTRY_LOFAR=${LOCAL_DOCKER_REGISTRY_LOFAR} + hostname: rcu2l-sim container_name: rcu2l-sim logging: driver: "json-file" diff --git a/docker-compose/sdptr-sim.yml b/docker-compose/sdptr-sim.yml index 8168c2fd00d51310e2eafe850833a6d11b3b5fc8..68c91fff0fd39f06c96ca6dbb4e100e8ac44bd89 100644 --- a/docker-compose/sdptr-sim.yml +++ b/docker-compose/sdptr-sim.yml @@ -15,6 +15,7 @@ services: args: - LOCAL_DOCKER_REGISTRY_HOST=${LOCAL_DOCKER_REGISTRY_HOST} - LOCAL_DOCKER_REGISTRY_LOFAR=${LOCAL_DOCKER_REGISTRY_LOFAR} + hostname: sdptr-sim container_name: sdptr-sim logging: driver: "json-file" diff --git a/docker-compose/tango-prometheus-exporter.yml b/docker-compose/tango-prometheus-exporter.yml index 318d4b64d39a16e1cd3815c7c3a0a786b1f7b336..5dfb01d55af66df97160f2e08de3ed9fceb36e6d 100644 --- a/docker-compose/tango-prometheus-exporter.yml +++ b/docker-compose/tango-prometheus-exporter.yml @@ -12,6 +12,7 @@ services: args: POLICY_FILE: lofar2-policy.json dockerfile: ./Dockerfile + hostname: tango-prometheus-exporter container_name: tango-prometheus-exporter logging: driver: "json-file" @@ -34,6 +35,7 @@ services: args: POLICY_FILE: lofar2-fast-policy.json dockerfile: ./Dockerfile + hostname: tango-prometheus-fast-exporter container_name: tango-prometheus-fast-exporter logging: driver: "json-file" diff --git a/docker-compose/tango-prometheus-exporter/code/tango-prometheus-client.py b/docker-compose/tango-prometheus-exporter/code/tango-prometheus-client.py index 7edd8d8d62a9ef5cb9b301711da1f7bea5f3e653..6ee05172f409deab8fb9a51fceb44447341c5e34 100644 --- a/docker-compose/tango-prometheus-exporter/code/tango-prometheus-client.py +++ b/docker-compose/tango-prometheus-exporter/code/tango-prometheus-client.py @@ -241,7 +241,7 @@ class CustomCollector(object): # obtain list of attributes to scrape attrs_to_scrape = self.policy.attribute_list(device_name, attr_infos.keys()) - logger.info(f"Processing device {device_name} attributes {attrs_to_scrape}") + logger.debug(f"Processing device {device_name} attributes {attrs_to_scrape}") # scrape each attribute metrics = [] @@ -290,18 +290,18 @@ class CustomCollector(object): reason = e.args[0].desc.replace("\n", " ") logger.warning(f"Error processing device {device_name}: {reason}") - # get the time since the last try, if less than a second, sleep for a bit. + # get the time since the last try, if less than a second, sleep for a bit. retry_wait_time = time.time() - last_exception_time if retry_wait_time < reconnect_timeout_time: time.sleep(reconnect_timeout_time - retry_wait_time) last_exception_time = time.time() - + except Exception as e: logger.exception(f"Error processing device {device_name}") finally: dev_scrape_end = time.time() - logger.info( + logger.debug( f"Done processing device {device_name}. Took {dev_scrape_end - dev_scrape_begin} seconds." ) diff --git a/docker-compose/tango-prometheus-exporter/lofar2-policy.json b/docker-compose/tango-prometheus-exporter/lofar2-policy.json index 46f97a31d1cedc343b68ffe3f034c17514fe8577..81c9a01d7410d193f5a859c7b274e5cdf7c22cd9 100644 --- a/docker-compose/tango-prometheus-exporter/lofar2-policy.json +++ b/docker-compose/tango-prometheus-exporter/lofar2-policy.json @@ -14,6 +14,12 @@ "Calibration_*" ] }, + "stat/antennafield/lba": { + "exclude": [ + "HBAT_*", + "RCU_DAB_filter_on_*" + ] + }, "stat/apsct/*": { }, "stat/ccd/1": { diff --git a/docker-compose/tango-rest.yml b/docker-compose/tango-rest.yml index 27f106dca2724b1890a42e8206d0e97f78579283..0505cd7bec8febde9c24342b6bba4eb05b79d2c9 100644 --- a/docker-compose/tango-rest.yml +++ b/docker-compose/tango-rest.yml @@ -16,12 +16,10 @@ version: '2.1' services: tango-rest: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-rest:${TANGO_REST_VERSION} + hostname: tango-rest container_name: tango-rest networks: - control - # set the hostname, otherwise duplicate device registrations result every - # time the hostname changes as the container is restarted. - hostname: tango-rest environment: - TANGO_HOST=${TANGO_HOST} ports: diff --git a/docker-compose/tango.yml b/docker-compose/tango.yml index e4fd11c5b5c2f5558a90d91930d43b9afcdcf8e4..58dfc1915fa772020b7bec50b722c9e31b89d83b 100644 --- a/docker-compose/tango.yml +++ b/docker-compose/tango.yml @@ -18,6 +18,7 @@ volumes: services: tangodb: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-db:${TANGO_DB_VERSION} + hostname: tangodb container_name: tangodb networks: - control @@ -40,6 +41,7 @@ services: databaseds: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-databaseds:${TANGO_DATABASEDS_VERSION} + hostname: databaseds container_name: databaseds networks: - control @@ -76,6 +78,7 @@ services: context: dsconfig args: SOURCE_IMAGE: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-dsconfig:${TANGO_DSCONFIG_VERSION} + hostname: dsconfig container_name: dsconfig networks: - control @@ -96,4 +99,3 @@ services: syslog-format: rfc3164 tag: "{{.Name}}" restart: unless-stopped - diff --git a/docker-compose/unb2-sim.yml b/docker-compose/unb2-sim.yml index 40e86946834472cab04b3048fe02ac77cc5ef456..89abb8ce69d70a0aac09f9ba8d30c30a1aa9ec93 100644 --- a/docker-compose/unb2-sim.yml +++ b/docker-compose/unb2-sim.yml @@ -15,6 +15,7 @@ services: args: - LOCAL_DOCKER_REGISTRY_HOST=${LOCAL_DOCKER_REGISTRY_HOST} - LOCAL_DOCKER_REGISTRY_LOFAR=${LOCAL_DOCKER_REGISTRY_LOFAR} + hostname: unb2-sim container_name: unb2-sim logging: driver: "json-file" diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh index f03e6f537a4afc44d0cc617fe05df8cc5a28c1c1..ba60f62eef6bb12bbbe15b7f36d7dc27b1651340 100755 --- a/sbin/run_integration_test.sh +++ b/sbin/run_integration_test.sh @@ -99,6 +99,7 @@ make start logstash http-json-schemas object-storage init-object-storage # Update the dsconfig # Do not remove `bash`, otherwise statement ignored by gitlab ci shell! bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/LOFAR_ConfigDb.json +bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/hierarchies/power.json bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/test_environment_ConfigDb.json bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/stations/simulators_ConfigDb.json bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/stations/dummy_positions_ConfigDb.json diff --git a/tangostationcontrol/VERSION b/tangostationcontrol/VERSION index a5510516948f37ef18bbb69d8edcb8a2c2e9ba7e..201a22c8fa5ccf5eee42a9f27552ad13e0a38619 100644 --- a/tangostationcontrol/VERSION +++ b/tangostationcontrol/VERSION @@ -1 +1 @@ -0.15.0 +0.16.2 diff --git a/tangostationcontrol/docs/source/devices/tilebeam-digitalbeam.rst b/tangostationcontrol/docs/source/devices/tilebeam-digitalbeam.rst index 63dcc687162834cf823628083c5e47ea39ea33b1..d3057ff03258294d4cb54246963499efb00b1bef 100644 --- a/tangostationcontrol/docs/source/devices/tilebeam-digitalbeam.rst +++ b/tangostationcontrol/docs/source/devices/tilebeam-digitalbeam.rst @@ -124,32 +124,23 @@ DigitalBeam The DigitalBeam device applies the following configuration to compute each beamlet. Here, ``N_ant := antennafield.nr_antennas_R`` and ``N_beamlet := NUM_BEAMLETS == N_output``. -:antenna_select_RW: Which beamlets to form with which antenna. ``True`` by default, except for antennas which are not mapped on any FPGA input (see "Configuration" below). +:Antenna_Set_RW: Which antenna set (supported by the antenna field) is requested to be beam formed. - :type: ``bool[N_ant][N_beamlet]`` - -:beamlet.subband_select_RW: Which subband to beamform for each beamlet. - - :type: ``uint32[N_beamlet]`` - -:sdp.clock_RW: Which clock to use (in Hz, default: 200e6). - - :type: ``uint32`` + :type: ``str`` -:sdp.antenna_type_R: Which antenna type is connected: "LBA" or "HBA". Configurable through the ``sdp.Antenna_Type`` property. +:Antenna_Mask_R: Which antennas are requested to be beam formed, according to the selected antenna set. - :type: ``str`` + :type: ``bool[N_ant]`` -:sdp.nyquist_zone_R: Relevant Nyquist zone (0, 1 or 2), based on the clock and antenna type. +:antennafield.Antenna_Usage_Mask_R: Which antennas are OK to be used (not broken, disabled, etc). - :type: ``uint32`` + :type: ``bool[N_ant]`` -Configuration -""""""""""""""""""""" -The following properties configure the DigitalBeam: +:beamlet.subband_select_RW: Which subband to beamform for each beamlet. -:Input_to_Antenna_Mapping: Which antenna from the AntennaField is the input for each antenna input of the FPGAs. + :type: ``uint32[N_beamlet]`` - :type: ``uint32[96]`` +:sdp.subband_frequency_R: Central frequency of each subband (in Hz). + :type: ``float`` diff --git a/tangostationcontrol/tangostationcontrol/clients/statistics/consumer.py b/tangostationcontrol/tangostationcontrol/clients/statistics/consumer.py index 62f2838f9944f0d6541f976a47a17ad7ab67d2fb..dea33f22a21077a68572fc1abf8d611b47c6e62e 100644 --- a/tangostationcontrol/tangostationcontrol/clients/statistics/consumer.py +++ b/tangostationcontrol/tangostationcontrol/clients/statistics/consumer.py @@ -72,7 +72,7 @@ class StatisticsConsumer(Thread, StatisticsClientThread): try: self.collector.process_packet(self.last_packet) except ValueError as e: - self._exception_logging() + self._exception_logging(e) # continue processing logger.info("Stopped statistics thread") diff --git a/tangostationcontrol/tangostationcontrol/common/lofar_logging.py b/tangostationcontrol/tangostationcontrol/common/lofar_logging.py index 74041f24b68029a737df94b5b6615b521960a73c..62a9b946681d4a0fd74dd190b5eaea87b2cdd0ae 100644 --- a/tangostationcontrol/tangostationcontrol/common/lofar_logging.py +++ b/tangostationcontrol/tangostationcontrol/common/lofar_logging.py @@ -37,11 +37,11 @@ class TangoLoggingHandler(logging.Handler): def emit(self, record): try: - if record.tango_device is None: + if record.device is None: # log record is not related to any device return except AttributeError: - # log record is not annotated with a tango_device + # log record is not annotated with a device return # determine which log stream to use @@ -50,11 +50,11 @@ class TangoLoggingHandler(logging.Handler): # send the log message to Tango try: record_msg = record.msg % record.args - stream(record.tango_device, record.msg, *record.args) + stream(record.device, record.msg, *record.args) except TypeError: # Tango's logger barfs on mal-formed log lines, f.e. if msg % args is not possible record_msg = f"{record.msg} {record.args}".replace("%", "%%") - stream(record.tango_device, record_msg) + stream(record.device, record_msg) self.flush() @@ -98,10 +98,10 @@ class LogSuppressErrorSpam(logging.Formatter): class LogAnnotator(logging.Formatter): """Annotates log records with: - record.tango_device: the Tango Device that is executing.""" + record.device: the Tango Device that is executing.""" @staticmethod - def get_current_tango_device() -> Device: + def get_current_device() -> Device: """Return the tango Device we're currently executing for, or None if it can't be detected. This is derived by traversing the stack and find a Device as 'self'. In some cases, @@ -115,10 +115,10 @@ class LogAnnotator(logging.Formatter): def filter(self, record): # annotate record with currently executing Tango device, if any - record.tango_device = self.get_current_tango_device() + record.device = self.get_current_device() # construct an identifier we can add for other devices as well - record.lofar_id = f"tango - {record.tango_device}" + record.lofar_id = f"tango - {record.device}" # annotate record with the current software version record.software_version = version @@ -166,7 +166,7 @@ def configure_logger(logger: logging.Logger = None, log_extra=None, debug=False) hostname = socket.gethostname() formatter = logging.Formatter( - fmt="%(asctime)s.%(msecs)d %(levelname)s - %(tango_device)s: %(message)s [%(funcName)s in %(filename)s:%(lineno)d]".format( + fmt="%(asctime)s.%(msecs)d %(levelname)s - %(device)s: %(message)s [%(funcName)s in %(filename)s:%(lineno)d]".format( hostname ), datefmt="%Y-%m-%dT%H:%M:%S", @@ -194,7 +194,7 @@ def configure_logger(logger: logging.Logger = None, log_extra=None, debug=False) ) # configure log messages - formatter = LogstashFormatter(extra=log_extra, tags=["python", "lofar"]) + formatter = LogstashFormatter(extra=log_extra, tags=[]) handler.setFormatter(formatter) handler.addFilter(LogSuppressErrorSpam()) handler.addFilter(LogAnnotator()) diff --git a/tangostationcontrol/tangostationcontrol/common/sdp.py b/tangostationcontrol/tangostationcontrol/common/sdp.py index 70bbfba11b7c7af159c9cac99fb2e4968f964d8a..4932699a3b4ff627d4cbaddcd699526af4e36f37 100644 --- a/tangostationcontrol/tangostationcontrol/common/sdp.py +++ b/tangostationcontrol/tangostationcontrol/common/sdp.py @@ -128,3 +128,13 @@ def complex_to_weights( return real_imag_to_weights( numpy.array(list(zip(complex_weights.real, complex_weights.imag))), unit ).reshape(complex_weights.shape) + + +def complex_to_weight( + complex_weight: complex, unit: int = SDP_UNIT_WEIGHT +) -> numpy.uint32: + """Pack a complex number in an FPGA weight (uint32). + + unit: the weight value representing a weight of 1.0.""" + + return complex_to_weights(numpy.array([complex_weight]), unit)[0] diff --git a/tangostationcontrol/tangostationcontrol/configuration/observation_settings.py b/tangostationcontrol/tangostationcontrol/configuration/observation_settings.py index e94c84c03538a15d8ee396138d94ff5e7f193243..19cf94f2b5a0bf684762fa19c3f4fbd7cf30682f 100644 --- a/tangostationcontrol/tangostationcontrol/configuration/observation_settings.py +++ b/tangostationcontrol/tangostationcontrol/configuration/observation_settings.py @@ -15,7 +15,6 @@ class ObservationSettings(_ConfigurationBase): observation_id: int, stop_time: datetime, antenna_set: str, - antenna_mask: Sequence[int], filter: str, SAPs: Sequence[Sap], tile_beam: Pointing = None, @@ -24,7 +23,6 @@ class ObservationSettings(_ConfigurationBase): self.observation_id = observation_id self.stop_time = stop_time self.antenna_set = antenna_set - self.antenna_mask = antenna_mask self.filter = filter self.SAPs = SAPs self.tile_beam = tile_beam @@ -35,7 +33,6 @@ class ObservationSettings(_ConfigurationBase): "observation_id": self.observation_id, "stop_time": self.stop_time.isoformat(), "antenna_set": self.antenna_set, - "antenna_mask": self.antenna_mask, "filter": self.filter, "SAPs": [dict(s) for s in self.SAPs], }.items() @@ -49,7 +46,6 @@ class ObservationSettings(_ConfigurationBase): json_dct["observation_id"], datetime.fromisoformat(json_dct["stop_time"]), json_dct["antenna_set"], - json_dct["antenna_mask"], json_dct["filter"], json_dct["SAPs"], json_dct["tile_beam"] if "tile_beam" in json_dct else None, diff --git a/tangostationcontrol/tangostationcontrol/devices/README.md b/tangostationcontrol/tangostationcontrol/devices/README.md index 5947ff53ba0816a1e55e828b96a2df313e71c3f7..df47d0be1e0b2f1cc11ae955cc2d082097a5d58a 100644 --- a/tangostationcontrol/tangostationcontrol/devices/README.md +++ b/tangostationcontrol/tangostationcontrol/devices/README.md @@ -7,6 +7,7 @@ This directory contains the sources for our custom Tango devices. If a new device is added, it will (likely) need to be referenced in several places. Adjust or add the following files (referenced from the repository root), following the pattern shown by the devices already there: - Adjust `CDB/LOFAR_ConfigDb.json` to create the device in the Tango device database, +- Add the device hierarchies in `CDB/hierarchies` to define the power, control and clock configuration, - Adjust `docker-compose/jupyterlab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py` to make an alias for it available in Jupyter-Lab, - Adjust `tangostationcontrol/tangostationcontrol/devices/boot.py` to add the device to the station initialisation sequence, - Add to `docker-compose/` to create a YaML file to start the device in a docker container. NOTE: it needs a unique 57xx port assigned (current _unused_ port value: 5727), a unique 58xx port for ZMQ events, and a unique 59xx port for ZMQ heartbeat diff --git a/tangostationcontrol/tangostationcontrol/devices/antennafield.py b/tangostationcontrol/tangostationcontrol/devices/antennafield.py index 87291d16225346f90cd9bd0d364de5d9badc9431..0477fef936e7d69dfb446f9bab9ecd066cde8590 100644 --- a/tangostationcontrol/tangostationcontrol/devices/antennafield.py +++ b/tangostationcontrol/tangostationcontrol/devices/antennafield.py @@ -16,6 +16,7 @@ from tango import ( DeviceProxy, DevSource, AttrWriteType, + DevVarBooleanArray, DevVarFloatArray, DevVarLongArray, DebugIt, @@ -47,7 +48,7 @@ from tangostationcontrol.common.lofar_logging import ( ) from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES from tangostationcontrol.common.type_checking import type_not_sequence -from tangostationcontrol.devices.device_decorators import fault_on_error, only_in_states +from tangostationcontrol.devices.device_decorators import only_in_states from tangostationcontrol.devices.interfaces.lofar_device import LOFARDevice logger = logging.getLogger() @@ -81,7 +82,6 @@ class MappedAttribute(attribute): ): if access == AttrWriteType.READ_WRITE: - @fault_on_error() def write_func_wrapper(device, value): cast_type = dtype while not type_not_sequence(cast_type): @@ -92,7 +92,6 @@ class MappedAttribute(attribute): self.fset = write_func_wrapper - @fault_on_error() def read_func_wrapper(device): return device.get_mapped_attribute(mapping_attribute, mapping_device) @@ -147,12 +146,14 @@ class AntennaField(LOFARDevice): doc="String representation of officially offered set of antennas", dtype="DevVarStringArray", mandatory=False, + default_value=["ALL"], ) Antenna_Set_Masks = device_property( doc="String encoding of the corresponding antenna masks for the antennafield", dtype="DevVarStringArray", mandatory=False, + default_value=["1" * MAX_ANTENNA], ) # ----- Antenna states @@ -200,7 +201,7 @@ class AntennaField(LOFARDevice): doc="Attenuation value to apply on all inputs.", dtype="DevFloat", mandatory=False, - default_value=0.0, + default_value=10.0, ) # ----- Position information @@ -344,17 +345,7 @@ class AntennaField(LOFARDevice): dtype=(str,), max_dim_x=MAX_ANTENNA, ) - Antenna_Set_RW = attribute( - doc="Name of the Antenna set", - dtype=str, - access=AttrWriteType.READ_WRITE, - ) - Antenna_Mask_RW = attribute( - doc="Mask values translated from the AntennaSet name", - dtype=(bool,), - max_dim_x=MAX_ANTENNA, - access=AttrWriteType.READ_WRITE, - ) + Antenna_to_SDP_Mapping_R = attribute( doc="To which (fpga, input) pair each antenna is connected. " "-1=unconnected.", dtype=((numpy.int32,),), @@ -679,36 +670,23 @@ class AntennaField(LOFARDevice): def read_Antenna_Names_R(self): return self.Antenna_Names - def read_Antenna_Set_RW(self): - return self._antenna_set + @command(dtype_in=str, dtype_out=DevVarBooleanArray) + def antenna_set_to_mask(self, antenna_set): + nr_antennas = self.read_attribute("nr_antennas_R") - def write_Antenna_Set_RW(self, antenna_set: str): - if antenna_set not in self.Antenna_Sets: - raise ValueError( - f"Unsupported antenna set : {antenna_set}. Must be one of {self.Antenna_Sets}" - ) - self._antenna_set = antenna_set - self._antenna_mask = self._translate_antenna_set_to_antenna_mask( - self._antenna_set - ) - - def read_Antenna_Mask_RW(self): - return self._antenna_mask - - def write_Antenna_Mask_RW(self, antenna_mask: list): - self._antenna_mask = antenna_mask - - def _translate_antenna_set_to_antenna_mask(self, antenna_set: str) -> numpy.ndarray: try: # Retrieve antenna_set entry position antenna_index = list(self.Antenna_Sets).index(antenna_set) - # Return the corresponding antenna_mask array + # Return the corresponding antenna_mask array, + # cut off for our number of antennas if needed, to support generic masks like "ALL" return numpy.array( - [int(x) for x in list(self.Antenna_Set_Masks[antenna_index])], + [int(x) for x in list(self.Antenna_Set_Masks[antenna_index])][ + :nr_antennas + ], dtype=bool, ) except ValueError as exc: - raise Exception( + raise ValueError( f"Unsupported antenna mask with the following antenna set: {antenna_set}. \ Must be one of {self.Antenna_Sets}" ) from exc @@ -1014,10 +992,6 @@ class AntennaField(LOFARDevice): self.__setup_all_proxies() self.__setup_recv_mapper() self.__setup_sdp_mapper() - self._antenna_set = "ALL" - self._antenna_mask = self._translate_antenna_set_to_antenna_mask( - self._antenna_set - ) @log_exceptions() def _prepare_hardware(self): diff --git a/tangostationcontrol/tangostationcontrol/devices/interfaces/beam_device.py b/tangostationcontrol/tangostationcontrol/devices/interfaces/beam_device.py index ba9dc3e6e5b10de401de84b3e8ba59b81afc1a6d..632967b2654596e62b2f9198a41d7d40dce5bbe5 100644 --- a/tangostationcontrol/tangostationcontrol/devices/interfaces/beam_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/interfaces/beam_device.py @@ -420,7 +420,7 @@ class BeamDevice(AsyncDevice): # Reshape the flatten input array pointing_direction = numpy.array(pointing_direction).reshape( - self._num_pointings, 3 + self._num_pointings, N_point_prop ) await self._set_pointing(pointing_direction, datetime.datetime.now()) @@ -445,7 +445,7 @@ class BeamDevice(AsyncDevice): # Reshape the flatten pointing array pointing_direction = numpy.array(pointing_direction).reshape( - self._num_pointings, 3 + self._num_pointings, N_point_prop ) await self._set_pointing(pointing_direction, timestamp) @@ -460,7 +460,7 @@ class BeamDevice(AsyncDevice): """ pointing_direction = numpy.array(pointing_direction).reshape( - self._num_pointings, 3 + self._num_pointings, N_point_prop ) delays = self._delays(pointing_direction, datetime.datetime.now()) diff --git a/tangostationcontrol/tangostationcontrol/devices/interfaces/hierarchy_device.py b/tangostationcontrol/tangostationcontrol/devices/interfaces/hierarchy_device.py index 3868f295507865a69bbec2b70e174e4561352622..d9841b95f0f9fc9be5a8e9fa71deda051bdb62ff 100644 --- a/tangostationcontrol/tangostationcontrol/devices/interfaces/hierarchy_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/interfaces/hierarchy_device.py @@ -71,7 +71,7 @@ class AbstractHierarchyDevice: self._hierarchy = AbstractHierarchy(child_property, children, parent, proxies) def children(self, depth: int = 1) -> AbstractHierarchy.children_type: - return self._hierarchy(depth) + return self._hierarchy.children(depth) def child(self, child_filter: str) -> Optional[DeviceProxy]: return self._hierarchy.child(child_filter) diff --git a/tangostationcontrol/tangostationcontrol/devices/interfaces/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/interfaces/lofar_device.py index 2ae6f1d2c368aa54c8fdcf00290891bb4b282751..a31b7447bb38bcf6b2a36e04a880eae52b1cea71 100644 --- a/tangostationcontrol/tangostationcontrol/devices/interfaces/lofar_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/interfaces/lofar_device.py @@ -24,7 +24,7 @@ from tango import ( ) # PyTango imports -from tango.server import attribute, command, Device +from tango.server import attribute, command, Device, device_property from tangostationcontrol.common.lofar_logging import log_exceptions from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES, INITIALISED_STATES from tangostationcontrol.common.type_checking import sequence_not_str @@ -69,6 +69,12 @@ class LOFARDevice(Device): The user triggers their transitions by the commands reflecting the target state (Initialise(), On(), Fault()). """ + # ----------------- + # Device Properties + # ----------------- + Power_Parent = device_property(dtype="DevString", mandatory=False) + Power_Children = device_property(dtype="DevVarStringArray", mandatory=False) + # ---------- # Attributes # ---------- diff --git a/tangostationcontrol/tangostationcontrol/devices/interfaces/power_hierarchy.py b/tangostationcontrol/tangostationcontrol/devices/interfaces/power_hierarchy.py index 5e0458099acdfda2f23be5f1a885ebd73f502746..13de540e34120e754244b2bbf34c44d9fce50b35 100644 --- a/tangostationcontrol/tangostationcontrol/devices/interfaces/power_hierarchy.py +++ b/tangostationcontrol/tangostationcontrol/devices/interfaces/power_hierarchy.py @@ -14,8 +14,8 @@ from tangostationcontrol.devices.interfaces.hierarchy_device import ( class PowerHierarchy(AbstractHierarchyDevice): - POWER_CHILD_PROPERTY = "power_children" - POWER_PARENT_PROPERTY = "power_parent" + POWER_CHILD_PROPERTY = "Power_Children" + POWER_PARENT_PROPERTY = "Power_Parent" def init( self, diff --git a/tangostationcontrol/tangostationcontrol/devices/interfaces/recv_device.py b/tangostationcontrol/tangostationcontrol/devices/interfaces/recv_device.py index 9b03afb856574666d79f18d5bc3e987907199c82..09c9456941b30821f459793fd84d6c47993c4591 100644 --- a/tangostationcontrol/tangostationcontrol/devices/interfaces/recv_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/interfaces/recv_device.py @@ -115,6 +115,7 @@ class RECVDevice(OPCUADevice): # ---------- ANT_mask_RW = AttributeWrapper( + doc="Which antennas are physically connected.", comms_annotation=["ANT_mask_RW"], datatype=bool, dims=(N_rcu, N_rcu_inp), diff --git a/tangostationcontrol/tangostationcontrol/devices/observation.py b/tangostationcontrol/tangostationcontrol/devices/observation.py index 28e3b46ffc240e8efda47a4c0fc8ebc41f6757a4..99bb46984ce7c8bd0ed8b0247bbe733ffc548ad7 100644 --- a/tangostationcontrol/tangostationcontrol/devices/observation.py +++ b/tangostationcontrol/tangostationcontrol/devices/observation.py @@ -71,9 +71,6 @@ class Observation(LOFARDevice): first_beamlet_R = attribute(dtype=numpy.int64, access=AttrWriteType.READ) observation_settings_RW = attribute(dtype=str, access=AttrWriteType.READ_WRITE) - antenna_mask_RW = attribute( - dtype=(numpy.int64,), max_dim_x=MAX_ANTENNA, access=AttrWriteType.READ_WRITE - ) def __init__(self, cl, name): super().__init__(cl, name) @@ -82,7 +79,6 @@ class Observation(LOFARDevice): self.digitalbeam_proxy: Optional[DeviceProxy] = None self.tilebeam_proxy: Optional[DeviceProxy] = None self._observation_settings: Optional[ObservationSettings] = None - self._antenna_mask = [] def init_device(self): """Setup some class member variables for observation state""" @@ -164,15 +160,9 @@ class Observation(LOFARDevice): ) # Apply Antenna Set - self.antennafield_proxy.Antenna_Set_RW = self._observation_settings.antenna_set - - # Apply Antenna Mask and Filter - self.write_antenna_mask_RW(self._observation_settings.antenna_mask) + self.digitalbeam_proxy.Antenna_Set_RW = self._observation_settings.antenna_set - ant_mask, frequency_band = self._apply_antennafield_settings( - self.read_antenna_mask_RW(), self.read_filter_R() - ) - self.antennafield_proxy.ANT_mask_RW = ant_mask + (frequency_band,) = self._apply_antennafield_settings(self.read_filter_R()) self.antennafield_proxy.Frequency_Band_RW = frequency_band # Apply Beamlet configuration @@ -184,6 +174,8 @@ class Observation(LOFARDevice): ) # Apply Tile Beam pointing direction + # NB: This is applied to all tiles, not just the ones in the selected + # antenna set. tile_beam = self.read_tile_beam_R() if tile_beam is not None: self.tilebeam_proxy.Pointing_direction_RW = [ @@ -279,19 +271,6 @@ class Observation(LOFARDevice): self._observation_settings = None # Except.throw_exception("IllegalCommand", e.message, __name__) - @fault_on_error() - @log_exceptions() - def read_antenna_mask_RW(self): - """Return current observation_parameters string""" - return self._antenna_mask - - @only_in_states([DevState.STANDBY, DevState.ON]) - @fault_on_error() - @log_exceptions() - def write_antenna_mask_RW(self, requested_antenna_mask: list): - """Write validated antenna mask values""" - self._antenna_mask = self._validate_antenna_mask(requested_antenna_mask) - @only_when_on() @fault_on_error() @log_exceptions() @@ -316,20 +295,14 @@ class Observation(LOFARDevice): ) return saps_pointing - def _apply_antennafield_settings(self, antenna_mask: list, filter_name: str): - """Convert an array of antenna indexes into a boolean mask array and - retrieve the RCU band from filter name, returning the correct format for + def _apply_antennafield_settings(self, filter_name: str): + """Retrieve the RCU band from filter name, returning the correct format for AntennaField device """ - ANT_mask_RW = [False] * MAX_ANTENNA - ANT_mask_RW = numpy.logical_or(ANT_mask_RW, antenna_mask) # convert numpy.array(dtype=str) to list[str] to avoid crash in DeviceProxy # see https://gitlab.com/tango-controls/pytango/-/issues/492 - return ( - numpy.array(ANT_mask_RW), - numpy.array([filter_name] * MAX_ANTENNA).tolist(), - ) + return (numpy.array([filter_name] * MAX_ANTENNA).tolist(),) def _apply_saps_subbands(self, sap_subbands: list): """Convert an array of subbands into the correct format for Beamlet device""" @@ -354,22 +327,6 @@ class Observation(LOFARDevice): """Convert the observation id value into the correct format for Antennafield device""" return numpy.array([observation_id] * MAX_ANTENNA, dtype=numpy.uint32) - def _validate_antenna_mask(self, requested_antenna_mask: list): - """Perform the validation steps for antenna_set and antenna_mask settings""" - antenna_mask = self.antennafield_proxy.Antenna_Mask_RW - antenna_usage_mask = self.antennafield_proxy.Antenna_Usage_Mask_R - # Bitwise AND of default antennafield mask and usable antennas - valid_antenna_mask = numpy.logical_and(antenna_mask, antenna_usage_mask) - # Bitwise AND of validate antenna mask and user-requested antennas - if len(requested_antenna_mask) == 0: - # Empty array = all antennas - return valid_antenna_mask - selected_antenna_mask = [0] * len(valid_antenna_mask) - for ant in requested_antenna_mask: - # select antenna only if it belongs to the valid antenna set - selected_antenna_mask[ant] = numpy.logical_and(1, valid_antenna_mask[ant]) - return selected_antenna_mask - # ---------- # Run server diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py index 736528674c77d28b2c614a513ad2d79c0e31681b..7c1356e4e7f4cf1451d0a60d8e1c56ffb692229b 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py @@ -368,6 +368,20 @@ class Beamlet(OPCUADevice): access=AttrWriteType.READ_WRITE, ) + # Co-polarization BF weights, using same weights for + # both X-polarization and Y-polarization, so w_xx = w_yy = w_pp + FPGA_bf_weights_pp_R = AttributeWrapper( + comms_annotation=["FPGA_bf_weights_pp_R"], + datatype=numpy.uint32, + dims=(N_pn, A_pn, N_beamlets_ctrl), + ) + FPGA_bf_weights_pp_RW = AttributeWrapper( + comms_annotation=["FPGA_bf_weights_pp_RW"], + datatype=numpy.uint32, + dims=(N_pn, A_pn, N_beamlets_ctrl), + access=AttrWriteType.READ_WRITE, + ) + # boolean[N_pn][N_beamsets_ctrl][P_sum] FPGA_bf_rx_align_stream_enable_R = AttributeWrapper( comms_annotation=["FPGA_bf_rx_align_stream_enable_R"], @@ -410,22 +424,6 @@ class Beamlet(OPCUADevice): dims=(N_pn, N_beamsets_ctrl, P_sum), ) - # cint16[N_pn][A_pn][N_beamlets_ctrl] - # Co-polarization BF weights, using same weights for - # both X-polarization and Y-polarization, so w_xx = w_yy = w_pp - FPGA_bf_weights_pp_R = AttributeWrapper( - comms_annotation=["FPGA_bf_weights_pp_R"], - datatype=numpy.uint32, - dims=(N_pn, A_pn, N_beamlets_ctrl), - ) - - FPGA_bf_weights_pp_RW = AttributeWrapper( - comms_annotation=["FPGA_bf_weights_pp_RW"], - datatype=numpy.uint32, - dims=(N_pn, A_pn, N_beamlets_ctrl), - access=AttrWriteType.READ_WRITE, - ) - subband_select_RW = attribute( dtype=(numpy.uint32,), max_dim_x=N_beamlets_ctrl, @@ -543,34 +541,34 @@ class Beamlet(OPCUADevice): @lru_cache() # this function requires large hardware reads for values that don't change often def _beamlet_frequencies(self): """Obtain the frequencies (in Hz) of each subband - that is selected for each input and beamlet. + that is selected for each antenna and beamlet. - Returns shape (fpga_nr, [input_nr][beamlet_nr]). + Returns shape (fpga_nr, input_nr, beamlet_nr), so one value per antenna, not per input. + This makes the result directly usable for FPGA_bf_weights_pp_RW. """ # obtain which subband is selected for each input and beamlet beamlet_subbands = self.read_attribute( "FPGA_beamlet_subband_select_RW" ).reshape( - N_pn, A_pn, N_pol, -1 + N_pn, A_pn, N_pol, N_beamlets_ctrl ) # orig: (fpga_nr, [input_nr][pol][beamlet_nr]) subband_frequencies = self.sdp_proxy.subband_frequency_R.reshape( N_pn, A_pn, N_pol, N_subbands ) # orig: ([fpga_nr][input_nr], subband_nr) - # Since we use same values for both polarizations, - # remove duplicated values (third dimension) - beamlet_subbands = beamlet_subbands[:, :, 0, :] - subband_frequencies = subband_frequencies[:, :, 0, :] - def frequencies_per_input(fpga_nr, antenna_nr): """Return the frequencies for the selected subbands of the given input.""" return numpy.take( - subband_frequencies[fpga_nr, antenna_nr], - beamlet_subbands[fpga_nr, antenna_nr], + subband_frequencies[fpga_nr, antenna_nr, 0, :], + beamlet_subbands[ + fpga_nr, antenna_nr, 0, : + ], # we use X pol for reference ) # compute all frequencies for all inputs. + # NB: since we assume both polarisations of each antenna have the same frequency + # in order to use the FPGA_bf_weights_pp_RW, we use the X polarisation for reference frequencies = numpy.array( [ frequencies_per_input(fpga_nr, antenna_nr) @@ -580,7 +578,7 @@ class Beamlet(OPCUADevice): dtype=numpy.float64, ) - return frequencies.reshape(N_pn, -1) + return frequencies.reshape(N_pn, A_pn, N_beamlets_ctrl) @staticmethod def _calculate_bf_weights( @@ -610,7 +608,7 @@ class Beamlet(OPCUADevice): """converts a difference in delays (in seconds) to a FPGA weight (in complex number)""" # Calculate the FPGA weight array - delays = delays.reshape(N_pn, A_pn * N_beamlets_ctrl) + delays = delays.reshape(N_pn, A_pn, N_beamlets_ctrl) beamlet_frequencies = self._beamlet_frequencies() bf_weights = self._calculate_bf_weights(delays, beamlet_frequencies) diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py b/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py index 0d6fd6f7730fce09f44996aab99edda7f7103e4e..8d0da8351685b31e9d46c87722a838d4956e1dd5 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py @@ -16,11 +16,11 @@ from tango.server import attribute, AttrWriteType, device_property from tangostationcontrol.beam.delays import Delays from tangostationcontrol.common.constants import ( N_beamlets_ctrl, - MAX_ANTENNA, N_xyz, A_pn, N_pn, - N_pol, + N_point_prop, + MAX_ANTENNA, ) # Additional import @@ -48,6 +48,8 @@ class DigitalBeam(BeamDevice): in the AntennaField's Antenna_Usage_Mask will be used in beamforming. Those disabled in the mask will get a weight of 0. + We always control all of the beamlets of the target Beamlet device. + This device using asyncio greenmode take care with async methods and awaiting them see :py:class:`BeamDevice` and :py:class:`AsyncDevice` for details. """ @@ -70,27 +72,32 @@ class DigitalBeam(BeamDevice): default_value="STAT/Beamlet/1", ) - Beamlet_Select = device_property( - dtype="DevVarBooleanArray", - doc="Array of beamlets to be controlled in the associated Beamlet device", - mandatory=False, - default_value=[True] * N_beamlets_ctrl, - ) - # ---------- # Attributes # ---------- + Antenna_Set_RW = attribute( + doc="Name of the antenna set to use for beam forming", + dtype=str, + access=AttrWriteType.READ_WRITE, + ) + + Antenna_Mask_R = attribute( + doc="Antennas selected for beam forming through Antenna_Set_RW", + dtype=(bool,), + max_dim_x=MAX_ANTENNA, + ) + Duration_delays_R = attribute( access=AttrWriteType.READ, dtype=numpy.float64, fget=lambda self: self._delays.get_statistic(self)["last"] or 0, ) - nr_inputs_R = attribute( - doc="Number of configured inputs from the associated antenna field.", + nr_antenas_R = attribute( + doc="Number of configured antennas from the associated antenna field.", dtype=numpy.uint32, - fget="nr_inputs", + fget="nr_antennas", ) nr_beamlets_R = attribute( @@ -106,32 +113,33 @@ class DigitalBeam(BeamDevice): fisallowed="is_attribute_access_allowed", ) - def nr_inputs(self): - """Return the number of configured inputs.""" + def nr_antennas(self): + """Return the number of configured antennas.""" - return len(self.antennafield_proxy.Antenna_to_SDP_Mapping_R) + return self.antennafield_proxy.nr_antennas_R def nr_beamlets(self): """Return the number of controlled beamlets.""" - return len([x for x in self.Beamlet_Select if x is True]) + + return N_beamlets_ctrl + + def read_Antenna_Set_RW(self): + return self._antenna_set + + def write_Antenna_Set_RW(self, antenna_set: str): + # make sure this antenna set is supported + _ = self.antennafield_proxy.antenna_set_to_mask(antenna_set) + + self._antenna_set = antenna_set + + def read_Antenna_Mask_R(self): + return self.antennafield_proxy.antenna_set_to_mask(self._antenna_set) def read_subband_select_RW(self): - """Beamlet attribute read filtered through Beamlet Select""" - subband_select = self.beamlet_proxy.subband_select_RW - for idx, beamlet in enumerate(self.Beamlet_Select): - if not beamlet: - subband_select[idx] = 0 - return subband_select + return self.beamlet_proxy.subband_select_RW def write_subband_select_RW(self, subbands): - """Beamlet attribute write filtered through Beamlet Select""" - subband_select = self.beamlet_proxy.subband_select_RW - assert subbands.shape == subband_select.shape - - for idx, beamlet in enumerate(self.Beamlet_Select): - if beamlet: - subband_select[idx] = subbands[idx] - self.beamlet_proxy.subband_select_RW = subband_select + self.beamlet_proxy.subband_select_RW = subbands # ---------- # Summarising Attributes @@ -155,123 +163,89 @@ class DigitalBeam(BeamDevice): self.beamlet_proxy = DeviceProxy(self.Beamlet_Device) self.beamlet_proxy.set_source(DevSource.DEV) + # Use all antennas by default + self.write_Antenna_Set_RW("ALL") + # Retrieve positions from RECV device reference_itrf = self.antennafield_proxy.Antenna_Field_Reference_ITRF_R antenna_itrf = self.antennafield_proxy.Antenna_Reference_ITRF_R.reshape( -1, N_xyz ) - # Generate positions for all FPGA inputs. - # Use reference position for any missing antennas so they always get a delay of 0 - input_itrf = numpy.array([reference_itrf] * MAX_ANTENNA) - for antenna_nr, (fpga_nr, input_nr) in enumerate( - self.antennafield_proxy.Antenna_to_SDP_Mapping_R - ): - if input_nr >= 0: - input_itrf[fpga_nr * A_pn + input_nr] = antenna_itrf[antenna_nr] - # a delay calculator self.delay_calculator = Delays(reference_itrf) # relative positions of each antenna - self.relative_input_positions = input_itrf - reference_itrf - - # configure which inputs are selected to be on - self._input_select() + self.relative_antenna_positions = antenna_itrf - reference_itrf # -------- # internal functions # -------- - def _input_select(self): - """ - Sets up the input selection - """ - - input_select = numpy.zeros((MAX_ANTENNA, N_beamlets_ctrl), dtype=bool) - - antenna_usage_mask = self.antennafield_proxy.Antenna_Usage_Mask_R - - for antenna_nr, (fpga_nr, input_nr) in enumerate( - self.antennafield_proxy.Antenna_to_SDP_Mapping_R - ): - if input_nr >= 0: - input_select[fpga_nr * A_pn + input_nr] = antenna_usage_mask[antenna_nr] - return input_select - @TimeIt() def _delays(self, pointing_direction: numpy.array, timestamp: datetime.datetime): """ Calculate the delays (in seconds) based on the pointing list and the timestamp, - Returns delays[antenna][beamlet] + Returns delays[N_pn][A_pn][len(pointing_direction)] """ - delays = numpy.zeros((MAX_ANTENNA, N_beamlets_ctrl), dtype=numpy.float64) - d = self.delay_calculator d.set_measure_time(timestamp) # calculate the delays based on the set reference position, # the set time and now the set direction and antenna positions - delays = d.delays_bulk(pointing_direction, self.relative_input_positions) + delays = d.delays_bulk(pointing_direction, self.relative_antenna_positions) return delays - def _map_inputs_on_polarised_inputs(self, arr): - """Converts an array with dimensions - [antenna][beamlet] -> [fpga_nr][input_nr][pol_nr][beamlet] - by repeating the values for both polarisations.""" - - assert arr.shape == (MAX_ANTENNA, N_beamlets_ctrl) - - # Each antenna maps on [fpga_nr][input_nr][0] and [fpga_nr][input_nr][1], and we work - # with [antenna][beamlet], so we have to interleave copies of the input array per beamlet - - # double the delays to cover both polarisations - # [[1,2,3], [4,5,6]] -> [[1,2,3,1,2,3], [4,5,6,4,5,6]] - result = numpy.hstack((arr, arr)) - - # move doubling of last dimension into first - # [[1,2,3,1,2,3], [4,5,6,4,5,6]] -> [[1,2,3], [1,2,3], [4,5,6], [4,5,6]] - result = result.reshape(result.shape[0] * 2, result.shape[1] // 2) - - # cast into (fpga_nr, [input_nr][pol_nr][beamlet]) - result = result.reshape((N_pn, A_pn * N_pol * N_beamlets_ctrl)) - - return result - - def _map_inputs_on_beamlet_selected_inputs(self, arr): - """Apply the Beamlet_Select property to the input array""" - assert arr.shape == (MAX_ANTENNA, self.nr_beamlets()) - beamlet_select = numpy.array(self.Beamlet_Select, dtype=bool) - assert beamlet_select.shape == (N_beamlets_ctrl,) - - result = numpy.zeros((MAX_ANTENNA, N_beamlets_ctrl), dtype=bool) - arr_index = 0 - for idx, beamlet in enumerate(beamlet_select): - if beamlet is True: - result[:, idx] = arr[:, arr_index] - arr_index += 1 - - return result - @TimeIt() def _compute_weights( self, pointing_direction: numpy.array, timestamp: datetime.datetime ) -> numpy.array: """ - Uploads beam weights based on a given pointing direction 2D array (96 tiles x 3 parameters) + Uploads beam weights based on a given pointing direction 2D array (488 beamlets x 3 parameters) """ - # Retrieve delays from casacore + assert pointing_direction.shape == ( + N_beamlets_ctrl, + N_point_prop, + ), f"{pointing_direction.shape}" + + # Retrieve delays from casacore, returns [antenna][beamlet] antenna_delays = self._delays(pointing_direction, timestamp) - # Map them onto the FPGA inputs - fpga_delays = self._map_inputs_on_beamlet_selected_inputs(antenna_delays) + # Map the antennas onto the SDP input. Inputs for which we do not have an + # antenna are given a delay of 0. They actually need a weight of 0, but no + # delay can accomplish that. We zero out weights later. + beam_delays = numpy.zeros((N_pn, A_pn, N_beamlets_ctrl), dtype=numpy.float32) + for antenna_nr, (fpga_nr, input_nr) in enumerate( + self.antennafield_proxy.Antenna_to_SDP_Mapping_R + ): + if input_nr >= 0: + beam_delays[fpga_nr, input_nr, :] = antenna_delays[antenna_nr, :] + + # Turn the delays into weights + beam_weights = self.beamlet_proxy.calculate_bf_weights(beam_delays.flatten()) + beam_weights = beam_weights.reshape(N_pn, A_pn, N_beamlets_ctrl) - beam_weights = self.beamlet_proxy.calculate_bf_weights(fpga_delays.flatten()) - beam_weights = beam_weights.reshape((N_pn, A_pn * N_beamlets_ctrl)) + # Use zero weights for antennas that we should not use: + # - if the antenna has no input mapped on the SDP + # - if the antenna is bad (False in Antenna_Usage_Mask_R) + # - if the antenna is not in the antenna set (False in Antenna_Mask_RW) + antenna_usage_mask = self.antennafield_proxy.Antenna_Usage_Mask_R + antenna_mask = self.read_Antenna_Mask_R() + zeroes_for_all_beamlets = numpy.array([0] * N_beamlets_ctrl, dtype=numpy.uint32) + + for antenna_nr, (fpga_nr, input_nr) in enumerate( + self.antennafield_proxy.Antenna_to_SDP_Mapping_R + ): + if ( + input_nr < 0 + or not antenna_usage_mask[antenna_nr] + or not antenna_mask[antenna_nr] + ): + beam_weights[fpga_nr, input_nr, :] = zeroes_for_all_beamlets return beam_weights @@ -283,14 +257,17 @@ class DigitalBeam(BeamDevice): beam_weights: numpy.array, ): """ - Uploads beam weights based on a given pointing direction 2D array (96 tiles x 3 parameters) + Uploads beam weights based on a given pointing direction 2D array (488 beamlets x 3 parameters) """ - self.atomic_read_modify_write_attribute( - beam_weights, - self.beamlet_proxy, - "FPGA_bf_weights_pp_RW", - self._input_select().reshape((N_pn, A_pn * N_beamlets_ctrl)), + assert beam_weights.shape == ( + N_pn, + A_pn, + N_beamlets_ctrl, + ), f"{beam_weights.shape}" + + self.beamlet_proxy.FPGA_bf_weights_pp_RW = beam_weights.reshape( + N_pn, A_pn * N_beamlets_ctrl ) # Determine how far away from ideal apply time diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py index 506e05ff4ecbb6768dea68d7c18547f0df84a3c2..db3f3718e741c2269a44fb020a369afaebfe8012 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py @@ -384,6 +384,11 @@ class SDP(OPCUADevice): datatype=numpy.double, dims=(N_pn, S_pn), ) + FPGA_signal_input_std_R = AttributeWrapper( + comms_annotation=["FPGA_signal_input_std_R"], + datatype=numpy.double, + dims=(N_pn, S_pn), + ) FPGA_jesd204b_csr_rbd_count_R = AttributeWrapper( comms_annotation=["FPGA_jesd204b_csr_rbd_count_R"], diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py index 5aa480788dc48e7b94f86a4c44ec4dd3a0b1a92c..3d5c5349483b07725df77d1145006237e23b493a 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py @@ -6,7 +6,7 @@ """ import numpy -from lofar_station_client.statistics.collector import StationSSTCollector +from lofar_station_client.statistics.collector import SSTCollector from attribute_wrapper.attribute_wrapper import AttributeWrapper # PyTango imports @@ -25,7 +25,7 @@ __all__ = ["SST", "main"] class SST(Statistics): - STATISTICS_COLLECTOR_CLASS = StationSSTCollector + STATISTICS_COLLECTOR_CLASS = SSTCollector # ----------------- # Device Properties diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/interfaces/__init__.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/interfaces/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..68ddd5cdc3efaa38e853aef337c08beb99c50c4c --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/interfaces/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy) +# SPDX-License-Identifier: Apache-2.0 diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/interfaces/test_power_hierarchy.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/interfaces/test_power_hierarchy.py new file mode 100644 index 0000000000000000000000000000000000000000..541549b358266bb43b10ace3eb6d369a102fcd8b --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/interfaces/test_power_hierarchy.py @@ -0,0 +1,82 @@ +# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy) +# SPDX-License-Identifier: Apache-2.0 + +from tango import DevState, DeviceProxy + +from tangostationcontrol.devices.interfaces.power_hierarchy import PowerHierarchy +from tangostationcontrol.integration_test import base +from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy + + +class TestPowerHierarchy(base.IntegrationTestCase): + stationmanager_name = "STAT/StationManager/1" + psoc_name = "STAT/PSOC/1" + sdp_name = "STAT/SDP/1" + + def setUp(self): + return super().setUp() + + def setup_stationmanager_proxy(self): + """Initialise StationManager device""" + stationmanager_proxy = TestDeviceProxy(self.stationmanager_name) + stationmanager_proxy.off() + power_properties = { + "Power_Children": ["STAT/PSOC/1", "STAT/PCON/1"], + } + stationmanager_proxy.put_property(power_properties) + stationmanager_proxy.boot() + self.assertEqual(stationmanager_proxy.state(), DevState.ON) + return stationmanager_proxy + + def setup_psoc_proxy(self): + """Initialise PSOC device""" + psoc_proxy = TestDeviceProxy(self.psoc_name) + psoc_proxy.off() + power_properties = { + "Power_Parent": ["STAT/StationManager/1"], + "Power_Children": ["STAT/CCD/1", "STAT/SDP/1"], + } + psoc_proxy.put_property(power_properties) + return psoc_proxy + + def setup_sdp_proxy(self): + """Initialise SDP device""" + sdp_proxy = TestDeviceProxy(self.sdp_name) + sdp_proxy.off() + power_properties = { + "Power_Parent": ["STAT/PSOC/1"], + "Power_Children": [ + "STAT/Beamlet/1", + "STAT/BST/1", + "STAT/SST/1", + "STAT/XST/1", + ], + } + sdp_proxy.put_property(power_properties) + return sdp_proxy + + def test_power_sequence_definition(self): + """ + Test whether Power Sequence is correctly retrieved from the HierarchyDevice + """ + self.setup_stationmanager_proxy() + self.setup_psoc_proxy() + self.setup_sdp_proxy() + stationmanager_ph = PowerHierarchy() + stationmanager_ph.init(self.stationmanager_name) + children_hierarchy = stationmanager_ph.children(depth=2) + # Check if PSOC is child of StationManager + self.assertTrue(self.psoc_name in children_hierarchy.keys()) + self.assertTrue( + isinstance(children_hierarchy["STAT/PSOC/1"]["proxy"], DeviceProxy) + ) + # Check if SDP is child of PSOC + self.assertTrue( + self.sdp_name in children_hierarchy["STAT/PSOC/1"]["children"].keys() + ) + # Check if PSOC retrieves correctly its parent state (StationManager -> ON) + psoc_ph = PowerHierarchy() + psoc_ph.init(self.psoc_name) + self.assertEqual(psoc_ph.parent_state(), DevState.ON) + # Check if child reads correctly a parent attribute + self.assertEqual(psoc_ph.read_parent_attribute("state")(), DevState.ON) diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py index ddb6f743fa487d40e2944225b252d479c1296431..2a0ca62af8dc82b1fae26ff7dbcee7a062088c3a 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beamlet.py @@ -12,8 +12,8 @@ from tangostationcontrol.common.constants import ( S_pn, CLK_200_MHZ, CLK_160_MHZ, - MAX_ANTENNA, N_pn, + A_pn, ) from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy @@ -55,7 +55,7 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): # The subband frequency of HBA subband 0 is 200 MHz, # so its period is 5 ns. A delay of 2.5e-9 seconds is thus half a period, # and result in a 180 degree phase offset. - delays = numpy.array([[2.5e-9] * MAX_ANTENNA] * N_beamlets_ctrl) + delays = numpy.array([[[2.5e-9] * N_pn] * A_pn] * N_beamlets_ctrl) calculated_bf_weights = self.proxy.calculate_bf_weights(delays.flatten()) @@ -63,7 +63,7 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): # which is 49152 when read as an uint32. self.assertEqual(-(2**14), c_short(49152).value) # check our calculations expected_bf_weights = numpy.array( - [49152] * MAX_ANTENNA * N_beamlets_ctrl, dtype=numpy.uint32 + [49152] * N_pn * A_pn * N_beamlets_ctrl, dtype=numpy.uint32 ) numpy.testing.assert_almost_equal(expected_bf_weights, calculated_bf_weights) @@ -82,14 +82,14 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): # The subband frequency of HBA subband 10 is 201953125 Hz # so its period is 4.95 ns ca, half period is 2.4758e-9 - delays = numpy.array([[2.4758e-9] * MAX_ANTENNA] * N_beamlets_ctrl) + delays = numpy.array([[[2.4758e-9] * N_pn] * A_pn] * N_beamlets_ctrl) calculated_bf_weights_subband_10 = self.proxy.calculate_bf_weights( delays.flatten() ) self.assertEqual(-(2**14), c_short(49152).value) # check our calculations expected_bf_weights_10 = numpy.array( - [49152] * MAX_ANTENNA * N_beamlets_ctrl, dtype=numpy.uint32 + [49152] * N_pn * A_pn * N_beamlets_ctrl, dtype=numpy.uint32 ) numpy.testing.assert_almost_equal( expected_bf_weights_10, calculated_bf_weights_subband_10 @@ -107,7 +107,7 @@ class TestDeviceBeamlet(AbstractTestBases.TestDeviceBase): self.proxy.on() # any non-zero delay should result in different weights for different clocks - delays = numpy.array([[2.5e-9] * MAX_ANTENNA] * N_beamlets_ctrl) + delays = numpy.array([[[2.5e-9] * N_pn] * A_pn] * N_beamlets_ctrl) sdp_proxy.clock_RW = CLK_200_MHZ time.sleep(1) # wait for beamlet device to process change event diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_calibration.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_calibration.py index 794368c496a80d6361aa5961e051e8bbd0a897dd..246cddd798dacc969c92bdf9f1f37bb69695e655 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_calibration.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_calibration.py @@ -75,6 +75,8 @@ class TestCalibrationDevice(AbstractTestBases.TestDeviceBase): "RECV_devices": ["STAT/RCU2H/1"], "Power_to_RECV_mapping": [1, 1, 1, 0] + [-1] * ((DEFAULT_N_HBA_TILES * 2) - 4), + "Antenna_Sets": ["ALL"], + "Antenna_Set_Masks": ["1" * DEFAULT_N_HBA_TILES], } ) self.recv_proxy = self.setup_recv_proxy() diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py index b09525433dbda88df57b49c8b9d5b0e466b6a103..cadb3af2c126ad766fee6e8af4d6ad9ce27c56a7 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py @@ -83,110 +83,20 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): NR_TILES = DEFAULT_N_HBA_TILES antennafield_proxy = TestDeviceProxy(self.antennafield_iden) control_mapping = [[1, i] for i in range(NR_TILES)] + sdp_mapping = [[i // 6, i % 6] for i in range(NR_TILES)] antennafield_proxy.put_property( { "RECV_devices": [self.recv_iden], + "SDP_device": self.sdp_iden, "Control_to_RECV_mapping": numpy.array(control_mapping).flatten(), + "Antenna_to_SDP_Mapping": numpy.array(sdp_mapping).flatten(), "Antenna_Quality": antenna_qualities, "Antenna_Use": antenna_use, "Antenna_Cables": ["50m", "80m"] * (DEFAULT_N_HBA_TILES // 2), - "Antenna_to_SDP_Mapping": [ - "0", - "0", - "0", - "1", - "0", - "2", - "0", - "3", - "0", - "4", - "0", - "5", - "1", - "0", - "1", - "1", - "1", - "2", - "1", - "3", - "1", - "4", - "1", - "5", - "2", - "0", - "2", - "1", - "2", - "2", - "2", - "3", - "2", - "4", - "2", - "5", - "3", - "0", - "3", - "1", - "3", - "2", - "3", - "3", - "3", - "4", - "3", - "5", - "4", - "0", - "4", - "1", - "4", - "2", - "4", - "3", - "4", - "4", - "4", - "5", - "5", - "0", - "5", - "1", - "5", - "2", - "5", - "3", - "5", - "4", - "5", - "5", - "6", - "0", - "6", - "1", - "6", - "2", - "6", - "3", - "6", - "4", - "6", - "5", - "7", - "0", - "7", - "1", - "7", - "2", - "7", - "3", - "7", - "4", - "7", - "5", + "Antenna_Sets": ["FIRST", "ALL"], + "Antenna_Set_Masks": [ + "1" + ("0" * (NR_TILES - 1)), + "1" * NR_TILES, ], } ) @@ -221,10 +131,8 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): ) # beam weights should now be non-zero, we don't actually check their values for correctness - FPGA_bf_weights_xx_yy_clock200 = ( - self.beamlet_proxy.FPGA_bf_weights_pp_RW.flatten() - ) - self.assertNotEqual(0, sum(FPGA_bf_weights_xx_yy_clock200)) + FPGA_bf_weights_pp_clock200 = self.beamlet_proxy.FPGA_bf_weights_pp_RW.flatten() + self.assertNotEqual(0, sum(FPGA_bf_weights_pp_clock200)) self.beamlet_proxy = self.initialise_beamlet_proxy() self.beamlet_proxy.on() @@ -233,12 +141,10 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): self.sdp_proxy.clock_RW = CLK_160_MHZ time.sleep(1) - FPGA_bf_weights_xx_yy_clock160 = ( - self.beamlet_proxy.FPGA_bf_weights_pp_RW.flatten() - ) + FPGA_bf_weights_pp_clock160 = self.beamlet_proxy.FPGA_bf_weights_pp_RW.flatten() # Assert some values are different self.assertNotEqual( - sum(FPGA_bf_weights_xx_yy_clock160), sum(FPGA_bf_weights_xx_yy_clock200) + sum(FPGA_bf_weights_pp_clock160), sum(FPGA_bf_weights_pp_clock200) ) def test_pointing_to_zenith_subband_change(self): @@ -267,7 +173,7 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): ).flatten() ) # Store values with first subband configuration - FPGA_bf_weights_xx_yy_subband_v1 = ( + FPGA_bf_weights_pp_subband_v1 = ( self.beamlet_proxy.FPGA_bf_weights_pp_RW.flatten() ) @@ -277,12 +183,12 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): self.beamlet_proxy.on() # Store values with second subband configuration - FPGA_bf_weights_xx_yy_subband_v2 = ( + FPGA_bf_weights_pp_subband_v2 = ( self.beamlet_proxy.FPGA_bf_weights_pp_RW.flatten() ) # Assert some values are different self.assertNotEqual( - sum(FPGA_bf_weights_xx_yy_subband_v1), sum(FPGA_bf_weights_xx_yy_subband_v2) + sum(FPGA_bf_weights_pp_subband_v1), sum(FPGA_bf_weights_pp_subband_v2) ) def test_set_pointing_masked_enable(self): @@ -297,15 +203,16 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): self.antenna_qualities_ok, self.antenna_use_ok ) - # Enable all inputs - self.antennafield_proxy.Antenna_Set_RW = "ALL" - self.proxy.initialise() self.proxy.Tracking_enabled_RW = False self.proxy.on() - all_zeros = numpy.array([[0] * (A_pn * N_beamlets_ctrl)] * N_pn) - self.beamlet_proxy.FPGA_bf_weights_pp_RW = all_zeros + # Enable first input + self.proxy.Antenna_Set_RW = "FIRST" + + # fill weights with values the beamformer will never use (|x| > 1) + impossible_values = numpy.array([[2] * N_beamlets_ctrl * A_pn] * N_pn) + self.beamlet_proxy.FPGA_bf_weights_pp_RW = impossible_values self.proxy.set_pointing( numpy.array( @@ -313,11 +220,27 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): ).flatten() ) - # Verify all zeros are replaced with other values for all inputs + # Verify all impossible values are replaced with other values for all inputs + # which should be non-zero for the first antenna, and zero for the rest + FPGA_bf_weights_pp_RW = self.beamlet_proxy.FPGA_bf_weights_pp_RW.reshape( + (N_pn * A_pn, -1) + ) + + # first antenna should have values from the beamformer, so not 0 and not 2 self.assertTrue( - numpy.any( - numpy.not_equal(all_zeros, self.beamlet_proxy.FPGA_bf_weights_pp_RW) - ) + numpy.all(numpy.not_equal(0, FPGA_bf_weights_pp_RW[0, :])), + f"{FPGA_bf_weights_pp_RW}", + ) + self.assertTrue( + numpy.all(numpy.not_equal(2, FPGA_bf_weights_pp_RW[0, :])), + f"{FPGA_bf_weights_pp_RW}", + ) + + # rest of the antennas should have been given a weight of 0, as they + # were not in the usage mask. + self.assertTrue( + numpy.all(numpy.equal(0, FPGA_bf_weights_pp_RW[1:DEFAULT_N_HBA_TILES, :])), + f"{FPGA_bf_weights_pp_RW}", ) @timeout_decorator.timeout(15) @@ -354,22 +277,3 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): ) logger.info("BeamTracking error: %s", error) time.sleep(interval) - - def test_subband_select_RW(self): - """Verify whether subband select attribute is correctly derived - from Beamlet Device""" - self.addCleanup(TestDeviceProxy.test_device_turn_off, self.beamlet_iden) - self.addCleanup(TestDeviceProxy.test_device_turn_off, self.sdp_iden) - self.addCleanup(TestDeviceProxy.test_device_turn_off, self.antennafield_iden) - - self.recv_proxy = self.setup_recv_proxy() - self.sdp_proxy = self.setup_sdp_proxy() - self.beamlet_proxy = self.initialise_beamlet_proxy() - self.beamlet_proxy.subband_select_RW = list(range(1, (N_beamlets_ctrl + 1))) - - beamlet_select = [False] + [True] * (N_beamlets_ctrl - 1) - self.proxy.off() - self.proxy.put_property({"Beamlet_Select": beamlet_select}) - self.proxy.warm_boot() - expected_subbands = [0] + list(range(2, (N_beamlets_ctrl + 1))) - numpy.testing.assert_equal(expected_subbands, self.proxy.subband_select_RW) diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_observation.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_observation.py index 3c1a35a0b48c422b17d9e4abbdc80fe065f075c9..dd375e946505894561cf34fbc2009a13a9266e15 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_observation.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_observation.py @@ -127,7 +127,6 @@ class TestDeviceObservation(AbstractTestBases.TestDeviceBase): self.beamlet_proxy = self.setup_beamlet_proxy() self.digitalbeam_proxy = self.setup_digitalbeam_proxy() self.tilebeam_proxy = self.setup_tilebeam_proxy() - self._antenna_mask = [1, 1, 1] + [0] * 6 + [1] + [0] * (MAX_ANTENNA - 10) def setup_recv_proxy(self): # setup RECV @@ -239,7 +238,6 @@ class TestDeviceObservation(AbstractTestBases.TestDeviceBase): stop_timestamp = datetime.fromisoformat(data["stop_time"]).timestamp() observation_id = data["observation_id"] antenna_set = data["antenna_set"] - antenna_mask = data["antenna_mask"] filter = data["filter"] num_saps = len(data["SAPs"]) saps_subband = [data["SAPs"][i]["subbands"] for i in range(0, num_saps)] @@ -274,16 +272,10 @@ class TestDeviceObservation(AbstractTestBases.TestDeviceBase): self.assertEqual(first_beamlet, self.proxy.first_beamlet_R) def test_apply_antennafield_settings(self): - """Test that attributes antenna_mask and filter are correctly applied""" + """Test that attribute filter is correctly applied""" self.setup_stationmanager_proxy() self.setup_recv_proxy() antennafield_proxy = self.setup_antennafield_proxy() - antennafield_proxy.ANT_mask_RW = [ - True - ] * DEFAULT_N_HBA_TILES # set all masks to True - self.assertListEqual( - antennafield_proxy.ANT_mask_RW.tolist(), [True] * DEFAULT_N_HBA_TILES - ) antennafield_proxy.RCU_band_select_RW = [0] * DEFAULT_N_HBA_TILES self.assertListEqual( antennafield_proxy.RCU_band_select_RW.tolist(), [0] * DEFAULT_N_HBA_TILES @@ -292,8 +284,6 @@ class TestDeviceObservation(AbstractTestBases.TestDeviceBase): self.proxy.observation_settings_RW = self.VALID_JSON self.proxy.Initialise() self.proxy.On() - expected_masks = [True, True, True] + [False] * 6 + [True] + [False] * 38 - self.assertListEqual(antennafield_proxy.ANT_mask_RW.tolist(), expected_masks) expected_bands = [ 2 ] * DEFAULT_N_HBA_TILES # we request every antenna to be in this band, regardless of mask diff --git a/tangostationcontrol/tangostationcontrol/integration_test/digitalbeam_performance/test_digitalbeam_performance.py b/tangostationcontrol/tangostationcontrol/integration_test/digitalbeam_performance/test_digitalbeam_performance.py index 137765c8a3ddb0cdd30f41f06e5007bfac1b8cf3..6e035960c8e62361c90a7e2a69eeb62bc206c728 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/digitalbeam_performance/test_digitalbeam_performance.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/digitalbeam_performance/test_digitalbeam_performance.py @@ -39,8 +39,7 @@ class TestDigitalbeamPerformance(base.IntegrationTestCase): beamlet_proxies.append(TestDeviceProxy(f"STAT/Beamlet/{i + 1}")) antenna_field_proxies.append(TestDeviceProxy(f"STAT/AntennaField/{item}")) beam_proxies.append(TestDeviceProxy(f"STAT/DigitalBeam/{item}")) - recv_proxies.append(TestDeviceProxy("STAT/RCU2H/1")) - recv_proxies.append(TestDeviceProxy("STAT/RCU2L/1")) + recv_proxies.append(TestDeviceProxy(f"STAT/RCU2H/{i + 1}")) for sdp in sdp_proxies + recv_proxies + beamlet_proxies: sdp.off() @@ -49,12 +48,13 @@ class TestDigitalbeamPerformance(base.IntegrationTestCase): sdp.set_defaults() self.assertTrue(sdp.state() is DevState.ON) - for n in range(1, 2): + for n in range(1, 3): proxy = antenna_field_proxies[n - 1] # setup AntennaField control_mapping = [[1, i] for i in range(MAX_ANTENNA)] antenna_qualities = numpy.array([AntennaQuality.OK] * MAX_ANTENNA) antenna_use = numpy.array([AntennaUse.AUTO] * MAX_ANTENNA) + antenna_set_mask = "1" * MAX_ANTENNA proxy.put_property( { "RECV_devices": [f"STAT/RCU2H/{n}"], @@ -62,6 +62,8 @@ class TestDigitalbeamPerformance(base.IntegrationTestCase): "Control_to_RECV_mapping": numpy.array(control_mapping).flatten(), "Antenna_Quality": antenna_qualities, "Antenna_Use": antenna_use, + "Antenna_Sets": ["ALL"], + "Antenna_Set_Masks": [antenna_set_mask], } ) proxy.off() diff --git a/tangostationcontrol/tangostationcontrol/test/devices/test_observation_base.py b/tangostationcontrol/tangostationcontrol/test/devices/test_observation_base.py index d46b19e8c875101a179564b9107069667d9a6196..a8740461ad0baeb2b5e1aff08a3ea9f556244616 100644 --- a/tangostationcontrol/tangostationcontrol/test/devices/test_observation_base.py +++ b/tangostationcontrol/tangostationcontrol/test/devices/test_observation_base.py @@ -8,7 +8,6 @@ class TestObservationBase: "observation_id": 12345, "stop_time": "2106-02-07T00:00:00", "antenna_set": "ALL", - "antenna_mask": [0,1,2,9], "filter": "HBA_110_190", "SAPs": [{ "subbands": [10, 20, 30], diff --git a/tangostationcontrol/test/common/test_lofar_logging.py b/tangostationcontrol/test/common/test_lofar_logging.py index 4af2e720736eb7424bd223c732114917d3c49804..ff541489cfac030fd96c138c9a557cf01211baa5 100644 --- a/tangostationcontrol/test/common/test_lofar_logging.py +++ b/tangostationcontrol/test/common/test_lofar_logging.py @@ -65,7 +65,7 @@ class TestLofarLogging(base.TestCase): logger = lofar_logging.configure_logger() logger.info("test") - self.assertIn("tango_device", self.memory_handler.records[0].__dict__) + self.assertIn("device", self.memory_handler.records[0].__dict__) self.assertIn("software_version", self.memory_handler.records[0].__dict__) @unittest.skip("Logs are not sent to Tango device currently, to reduce logspam") @@ -108,7 +108,7 @@ class TestLofarLogging(base.TestCase): # we can't compare them direclty. Just verify we're talking about the same thing. test_object.assertEqual( str(self), - str(test_record[0].tango_device), + str(test_record[0].device), msg="configure_logging did not detect active Tango device", ) diff --git a/tangostationcontrol/test/common/test_observation_controller.py b/tangostationcontrol/test/common/test_observation_controller.py index 0de8ae33ba9c8d16eb49c96fc10fbf6380c48a1e..649d22590c156d67771c46b6e6156a0be404f424 100644 --- a/tangostationcontrol/test/common/test_observation_controller.py +++ b/tangostationcontrol/test/common/test_observation_controller.py @@ -43,7 +43,6 @@ class TestRunningObservation(base.TestCase): 5, datetime.fromisoformat("2022-10-26T11:35:54.704150"), "ALL", - [3, 2, 1], "filter settings", [Sap([3, 2], Pointing(1.2, 2.1, "LMN")), Sap([1], Pointing(3.3, 4.4, "MOON"))], ) diff --git a/tangostationcontrol/test/common/test_sdp.py b/tangostationcontrol/test/common/test_sdp.py index afbc0a29ba8db196272a0c2f44aab923104282f8..7f575185ac50b2dc8937409e7808680a87e90a6d 100644 --- a/tangostationcontrol/test/common/test_sdp.py +++ b/tangostationcontrol/test/common/test_sdp.py @@ -152,7 +152,7 @@ class TestSDPCommon(base.TestCase): def test_weight_to_complex(self): unit = 8192 - # some trivial values + self.assertEqual(0, weight_to_complex(0, unit)) self.assertEqual(1, weight_to_complex(unit, unit)) self.assertEqual(1j, weight_to_complex(unit << 16, unit)) diff --git a/tangostationcontrol/test/configuration/_mock_requests.py b/tangostationcontrol/test/configuration/_mock_requests.py index 01f6190a82a1ac22173d693d27f8684de17d5fd8..cf9c177004e6f48fe90f9796aaf1501f98ea9c63 100644 --- a/tangostationcontrol/test/configuration/_mock_requests.py +++ b/tangostationcontrol/test/configuration/_mock_requests.py @@ -80,7 +80,6 @@ OBSERVATION_SETTINGS_SCHEMA = """ "observation_id", "stop_time", "antenna_set", - "antenna_mask", "filter", "SAPs" ], @@ -96,13 +95,6 @@ OBSERVATION_SETTINGS_SCHEMA = """ "antenna_set": { "type": "string" }, - "antenna_mask": { - "type": "array", - "minItems": 1, - "items": { - "type": "number" - } - }, "filter": { "type": "string" }, diff --git a/tangostationcontrol/test/configuration/test_observation_settings.py b/tangostationcontrol/test/configuration/test_observation_settings.py index 7002c9c63962f4e885a62f2be2081f2c606b4532..4a3effeaa159034480b4f017920615e68737053f 100644 --- a/tangostationcontrol/test/configuration/test_observation_settings.py +++ b/tangostationcontrol/test/configuration/test_observation_settings.py @@ -18,20 +18,19 @@ class TestObservationSettings(base.TestCase): def test_from_json(self, _): sut = ObservationSettings.from_json( '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", ' - '"antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings",' + '"antenna_set": "ALL", "filter": "filter_settings",' '"SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}]}' ) self.assertEqual(sut.observation_id, 3) self.assertEqual(sut.stop_time, datetime.fromisoformat("2012-04-23T18:25:43")) self.assertEqual(sut.antenna_set, "ALL") - self.assertEqual(sut.antenna_mask, [3, 2, 1]) self.assertEqual(sut.filter, "filter_settings") self.assertEqual(len(sut.SAPs), 1) sut = ObservationSettings.from_json( '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", ' - '"antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings",' + '"antenna_set": "ALL", "filter": "filter_settings",' '"SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],' '"tile_beam": {"angle1":2.2, "angle2": 3.1, "direction_type":"MOON"} }' ) @@ -42,7 +41,7 @@ class TestObservationSettings(base.TestCase): sut = ObservationSettings.from_json( '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", ' - '"antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings",' + '"antenna_set": "ALL", "filter": "filter_settings",' '"SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],' '"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}' ) @@ -51,32 +50,40 @@ class TestObservationSettings(base.TestCase): def test_from_json_type_missmatch(self, _): for json in [ - '{"observation_id": "3", "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "test", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "test", "antenna_set": 4, "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": ["3", 2, 1], "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": "3", "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": 1,"SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": {"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}},"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - # '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": [1],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - # '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": 1, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": "2"}', + # observation_id + '{"observation_id": "3", "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # stop_time + '{"observation_id": 3, "stop_time": "test", "antenna_set": "ALL", "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # antenna_set + '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": 4, "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # filter + '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "filter": 1,"SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # SAPs + '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "filter": "filter_settings","SAPs": {"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}},"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "filter": "filter_settings","SAPs": [1],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": 1, "first_beamlet": 2}', + # first_beamlet + '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": "2"}', ]: - with self.assertRaises((ValidationError, ValueError)): + with self.assertRaises((ValidationError, ValueError), msg=f"{json}"): ObservationSettings.from_json(json) def test_from_json_missing_fields(self, _): for json in [ - '{"stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [], "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings","tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', - '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings","SAPs": [],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # observation_id + '{"stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # stop_time + '{"observation_id": 3, "antenna_set": "ALL", "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # antenna_set + '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "filter": "filter_settings","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # filter + '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # SAPs + '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "filter": "filter_settings","tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', + # SAPs (empty list) + '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_set": "ALL", "filter": "filter_settings","SAPs": [],"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}, "first_beamlet": 2}', ]: - with self.assertRaises((ValidationError, ValueError)): + with self.assertRaises((ValidationError, ValueError), msg=f"{json}"): ObservationSettings.from_json(json) def test_to_json(self, _): @@ -84,7 +91,6 @@ class TestObservationSettings(base.TestCase): 5, datetime.fromisoformat("2022-10-26T11:35:54.704150"), "ALL", - [3, 2, 1], "filter settings", [ Sap([3, 2], Pointing(1.2, 2.1, "LMN")), @@ -94,7 +100,7 @@ class TestObservationSettings(base.TestCase): self.assertEqual( sut.to_json(), '{"observation_id": 5, "stop_time": "2022-10-26T11:35:54.704150", ' - '"antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter settings", "SAPs": ' + '"antenna_set": "ALL", "filter": "filter settings", "SAPs": ' '[{"subbands": [3, 2], "pointing": {"angle1": 1.2, "angle2": 2.1, ' '"direction_type": "LMN"}}, {"subbands": [1], "pointing": {"angle1": 3.3, ' '"angle2": 4.4, "direction_type": "MOON"}}], "first_beamlet": 0}', @@ -106,7 +112,7 @@ class TestObservationSettings(base.TestCase): with self.assertRaises(RefResolutionError): ObservationSettings.from_json( '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", ' - '"antenna_set": "ALL", "antenna_mask": [3, 2, 1], "filter": "filter_settings",' + '"antenna_set": "ALL", "filter": "filter_settings",' '"SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}]}' ) self.assertEqual(5, mock_get.call_count) diff --git a/tangostationcontrol/test/devices/interfaces/test_beam_device.py b/tangostationcontrol/test/devices/interfaces/test_beam_device.py index b93d2fa8d6b0a5a5b492765f4eae1ee251ffc60f..32bb619d1b69b9af2cc44405ba569fceea347707 100644 --- a/tangostationcontrol/test/devices/interfaces/test_beam_device.py +++ b/tangostationcontrol/test/devices/interfaces/test_beam_device.py @@ -27,7 +27,7 @@ class TestBeamDevice(device_base.DeviceTestCase): super(TestBeamDevice, self).setUp() -def default_update_callback(next_update): +async def default_update_callback(next_update): pass @@ -142,7 +142,7 @@ class TestBeamTracker(base.TestCase): updates = [] # Sleep randomly based on error to emulate execution variance - def update_store_callback(next_update): + async def update_store_callback(next_update): time.sleep(random.random() * error) updates.append(datetime.datetime.now()) @@ -195,7 +195,7 @@ class TestBeamTracker(base.TestCase): preparation_time = 0.1 updates = [] - def update_fast_callback(next_update): + async def update_fast_callback(next_update): """Make callback much faster than preparation_time""" # Do not measure actual time but intended interval here @@ -230,7 +230,7 @@ class TestBeamTracker(base.TestCase): interval = 0.2 updates = [] - def update_callback(next_update): + async def update_callback(next_update): """Append immediately""" # Do not measure actual time but intended interval here @@ -275,7 +275,7 @@ class TestBeamTracker(base.TestCase): preparation_time = 0.2001 updates = [] - def update_sleepy_callback(next_update): + async def update_sleepy_callback(next_update): """Append after preparation_time""" time.sleep(preparation_time) updates.append(next_update) @@ -312,7 +312,7 @@ class TestBeamTracker(base.TestCase): preparation_time = 0.2 updates = [] - def update_sleepy_callback(next_update): + async def update_sleepy_callback(next_update): """Append after preparation_time""" time.sleep(preparation_time) @@ -350,7 +350,7 @@ class TestBeamTracker(base.TestCase): preparation_time = 0.01 updates = [] - def update_throwing_callback(next_update): + async def update_throwing_callback(next_update): updates.append(next_update) raise DevFailed("foo") diff --git a/tangostationcontrol/test/devices/sdp/test_digitalbeam_device.py b/tangostationcontrol/test/devices/sdp/test_digitalbeam_device.py index 253dd7207a5946617627d49746336090c1e8ad51..4088b766487845714d25382a71774c09e7f2928d 100644 --- a/tangostationcontrol/test/devices/sdp/test_digitalbeam_device.py +++ b/tangostationcontrol/test/devices/sdp/test_digitalbeam_device.py @@ -110,14 +110,3 @@ class TestDigitalBeamDevice(device_base.DeviceTestCase): numpy.testing.assert_equal( m_proxy.return_value.write_attribute.call_args[0][0], new_data ) - - def test_read_nr_beamlets(self): - """Test whether the number of beamlets are correctly read""" - beamlet_select = [False] + [True] * (N_beamlets_ctrl - 1) - db_properties = {"Beamlet_Select": beamlet_select} - with DeviceTestContext( - digitalbeam.DigitalBeam, - properties={**db_properties}, - process=True, - ) as proxy: - self.assertEqual(proxy.nr_beamlets_R, N_beamlets_ctrl - 1) diff --git a/tangostationcontrol/test/devices/test_antennafield_device.py b/tangostationcontrol/test/devices/test_antennafield_device.py index 8aa6e781ae0e97a25dd63509f35219d8846a8fa7..196ff4389425110df4bbcb090680cf1677c7a692 100644 --- a/tangostationcontrol/test/devices/test_antennafield_device.py +++ b/tangostationcontrol/test/devices/test_antennafield_device.py @@ -890,24 +890,22 @@ class TestAntennafieldDevice(device_base.DeviceTestCase): m_proxy.return_value.write_attribute.call_args[0][1], data ) - def test_read_Antenna_Mask_all_antennas(self): + def test_Antenna_Mask_all_antennas(self): """Verify if Antenna_Mask_R is correctly retrieved""" with DeviceTestContext( antennafield.AntennaField, properties={**self.AT_PROPERTIES, **self.ANTENNA_PROPERTIES}, process=True, ) as proxy: - proxy.Antenna_Set_RW = "ALL" expected = [True] * MAX_ANTENNA - numpy.testing.assert_equal(expected, proxy.Antenna_Mask_RW) + numpy.testing.assert_equal(expected, proxy.antenna_set_to_mask("ALL")) - def test_read_Antenna_Mask_half_antennas(self): + def test_Antenna_Mask_half_antennas(self): """Verify if Antenna_Mask_R is correctly retrieved""" with DeviceTestContext( antennafield.AntennaField, properties={**self.AT_PROPERTIES, **self.ANTENNA_PROPERTIES}, process=True, ) as proxy: - proxy.Antenna_Set_RW = "INNER" expected = [True] * int(MAX_ANTENNA / 2) + [False] * int(MAX_ANTENNA / 2) - numpy.testing.assert_equal(expected, proxy.Antenna_Mask_RW) + numpy.testing.assert_equal(expected, proxy.antenna_set_to_mask("INNER")) diff --git a/tangostationcontrol/tox.ini b/tangostationcontrol/tox.ini index 6cab77e9ff5d26feaa240300559bd80a128d4b22..e0e446e4c41a01628dde2952b2f282a8ee66188d 100644 --- a/tangostationcontrol/tox.ini +++ b/tangostationcontrol/tox.ini @@ -59,7 +59,7 @@ setenv = commands = {envpython} --version {envpython} -m pytest --version - {envpython} -m pytest test --cov-report xml --cov-report html --cov-report xml:coverage.xml --cov=test + {envpython} -m pytest test --cov-report term --cov-report html --cov-report xml:coverage.xml --cov=tangostationcontrol # Use generative name and command prefixes to reuse the same virtualenv # for all linting jobs.