diff --git a/bin/wait-for-it.sh b/bin/wait-for-it.sh new file mode 100755 index 0000000000000000000000000000000000000000..0bd0c104de461817575aafc4962fcc5ee786945e --- /dev/null +++ b/bin/wait-for-it.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2086,SC2064,SC2206,SC2124 +# Use this script to test if a given TCP host/port are available +# +# Source: https://github.com/vishnubob/wait-for-it +# License: MIT + +cmdname=$(basename $0) + +echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $TIMEOUT -gt 0 ]]; then + echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT" + else + echoerr "$cmdname: waiting for $HOST:$PORT without a timeout" + fi + start_ts=$(date +%s) + while : + do + (echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1 + result=$? + if [[ $result -eq 0 ]]; then + end_ts=$(date +%s) + echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds" + break + fi + sleep 1 + done + return $result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $QUIET -eq 1 ]]; then + timeout $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT & + else + timeout $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT & + fi + PID=$! + trap "kill -INT -$PID" INT + wait $PID + RESULT=$? + if [[ $RESULT -ne 0 ]]; then + echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT" + fi + return $RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + hostport=(${1//:/ }) + HOST=${hostport[0]} + PORT=${hostport[1]} + shift 1 + ;; + --child) + CHILD=1 + shift 1 + ;; + -q | --quiet) + QUIET=1 + shift 1 + ;; + -s | --strict) + STRICT=1 + shift 1 + ;; + -h) + HOST="$2" + if [[ $HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + HOST="${1#*=}" + shift 1 + ;; + -p) + PORT="$2" + if [[ $PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + PORT="${1#*=}" + shift 1 + ;; + -t) + TIMEOUT="$2" + if [[ $TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + CLI="$@" + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$HOST" == "" || "$PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +TIMEOUT=${TIMEOUT:-15} +STRICT=${STRICT:-0} +CHILD=${CHILD:-0} +QUIET=${QUIET:-0} + +if [[ $CHILD -gt 0 ]]; then + wait_for + RESULT=$? + exit $RESULT +else + if [[ $TIMEOUT -gt 0 ]]; then + wait_for_wrapper + RESULT=$? + else + wait_for + RESULT=$? + fi +fi + +if [[ $CLI != "" ]]; then + if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then + echoerr "$cmdname: strict mode, refusing to execute subprocess" + exit $RESULT + fi + exec $CLI +else + exit $RESULT +fi diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh index 1dc410d55bd9a81b45490fbb867f2bd0927ced91..343eac0520e9e4ece9a4e7144e8ebd1b1e165753 100755 --- a/sbin/run_integration_test.sh +++ b/sbin/run_integration_test.sh @@ -11,20 +11,26 @@ fi cd "$LOFAR20_DIR/docker-compose" || exit 1 +# Start the database server first +make build databaseds dsconfig +make start databaseds dsconfig + +# Give dsconfig and databaseds time to start +sleep 1 # dsconfig container must be up and running... +# shellcheck disable=SC2016 +echo '/usr/local/bin/wait-for-it.sh ${TANGO_HOST} --strict --timeout=300 -- true' | make run dsconfig bash - + # Build only the required images, please do not build everything that makes CI # take really long to finish, especially grafana / jupyter / prometheus. # jupyter is physically large > 2.5gb and overlayfs is really slow. make build device-sdp device-recv device-sst device-unb2 device-xst device-beamlet device-digitalbeam device-tilebeam make build sdptr-sim recv-sim unb2-sim apsct-sim apspu-sim -make build databaseds dsconfig elk integration-test +make build elk integration-test make build archiver-timescale hdbppts-cm hdbppts-es # Start and stop sequence make stop device-boot device-docker device-apsct device-apspu device-sdp device-recv device-sst device-unb2 device-xst device-beamlet device-digitalbeam device-tilebeam sdptr-sim recv-sim unb2-sim apsct-sim apspu-sim hdbppts-es hdbppts-cm archiver-timescale -make start databaseds dsconfig elk - -# Give dsconfig and databaseds time to start -sleep 60 +make start elk # Update the dsconfig # Do not remove `bash`, otherwise statement ignored by gitlab ci shell! diff --git a/tangostationcontrol/tangostationcontrol/devices/boot.py b/tangostationcontrol/tangostationcontrol/devices/boot.py index 0b496636fbd30550255c488ef458e294468c07aa..59427165c20f5c8f9a65e9f59b0d85c4a3fa8837 100644 --- a/tangostationcontrol/tangostationcontrol/devices/boot.py +++ b/tangostationcontrol/tangostationcontrol/devices/boot.py @@ -50,7 +50,7 @@ class DevicesInitialiser(object): the start() method, and progress can be followed by inspecting the members progress (0-100), status (string), and is_running() (bool). """ - def __init__(self, device_names, reboot=False, initialise_hardware=True, proxy_timeout=10.0): + def __init__(self, device_names, reboot=False, initialise_hardware=True, proxy_timeout=60.0): self.reboot = reboot self.initialise_hardware = initialise_hardware @@ -218,7 +218,7 @@ class Boot(lofar_device): DeviceProxy_Time_Out = device_property( dtype='DevDouble', mandatory=False, - default_value=30.0, + default_value=60.0, ) # Initialise the hardware when initialising a station. Can end badly when using simulators. diff --git a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py index fc5c661b7937bf2489da23e6ed000f2050e97630..50368d15d59fd94794edfaafdb2c451513b60b24 100644 --- a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py @@ -314,6 +314,7 @@ class lofar_device(Device, metaclass=DeviceMeta): @only_in_states([DevState.STANDBY, DevState.INIT, DevState.ON]) @fault_on_error() @command() + @DebugIt() def prepare_hardware(self): """ Load firmware required before configuring anything. """ @@ -323,6 +324,7 @@ class lofar_device(Device, metaclass=DeviceMeta): @only_in_states([DevState.STANDBY, DevState.INIT, DevState.ON]) @fault_on_error() @command() + @DebugIt() def initialise_hardware(self): """ Initialise the hardware after configuring it. """ diff --git a/tangostationcontrol/tangostationcontrol/devices/recv.py b/tangostationcontrol/tangostationcontrol/devices/recv.py index 8836e2c9415be916625edf089a292d0fe945d6f1..1c21c006221289d2847033f9830fef8760009c3e 100644 --- a/tangostationcontrol/tangostationcontrol/devices/recv.py +++ b/tangostationcontrol/tangostationcontrol/devices/recv.py @@ -126,6 +126,18 @@ class RECV(opcua_device): # ----- Position information + Antenna_Field_Reference_ITRF = device_property( + doc="ITRF position (XYZ) of each antenna field", + dtype='DevVarFloatArray', + mandatory=False + ) + + Antenna_Field_Reference_ETRS = device_property( + doc="ETRS position (XYZ) of each antenna field", + dtype='DevVarFloatArray', + mandatory=False + ) + HBAT_reference_ITRF = device_property( doc="ITRF position (XYZ) of each HBAT (leave empty to auto-derive from ETRS)", dtype='DevVarFloatArray', @@ -218,6 +230,10 @@ class RECV(opcua_device): # ----- Position information + Antenna_Field_Reference_ITRF_R = attribute(access=AttrWriteType.READ, + doc='Absolute reference position of antenna field, in ITRF', + dtype=(numpy.float,), max_dim_x=3) + HBAT_antenna_ITRF_offsets_R = attribute(access=AttrWriteType.READ, doc='Offsets of the antennas within a tile, in ITRF ("iHBADeltas"). True shape: 96x16x3.', dtype=((numpy.float,),), max_dim_x=48, max_dim_y=96) @@ -226,6 +242,15 @@ class RECV(opcua_device): doc='Absolute reference position of each tile, in ITRF', dtype=((numpy.float,),), max_dim_x=3, max_dim_y=96) + def read_Antenna_Field_Reference_ITRF_R(self): + # provide ITRF field coordinates if they were configured + if self.Antenna_Field_Reference_ITRF: + return numpy.array(self.Antenna_Field_Reference_ITRF).reshape(3) + + # calculate them from ETRS coordinates if not, using the configured ITRF reference + ETRS_coordinates = numpy.array(self.Antenna_Field_Reference_ETRS).reshape(3) + return ETRS_to_ITRF(ETRS_coordinates, self.ITRF_Reference_Frame, self.ITRF_Reference_Epoch) + def read_HBAT_antenna_ITRF_offsets_R(self): base_antenna_offsets = numpy.array(self.HBAT_base_antenna_offsets).reshape(16,3) PQR_to_ETRS_rotation_matrix = numpy.array(self.HBAT_PQR_to_ETRS_rotation_matrix).reshape(3,3) @@ -252,7 +277,7 @@ class RECV(opcua_device): # ---------- # Summarising Attributes # ---------- - RCU_LED_colour_R = attribute(dtype=(numpy.uint32,), max_dim_x=32) + RCU_LED_colour_R = attribute(dtype=(numpy.uint32,), max_dim_x=32, fisallowed="is_attribute_wrapper_allowed") def read_RCU_LED_colour_R(self): return (2 * self.read_attribute("RCU_LED_green_on_R") + 4 * self.read_attribute("RCU_LED_red_on_R")).astype(numpy.uint32) diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py index e490722440967062030e20df8ba0dec97b2ffb78..dbbc644364cab4706f2b46e21b798b671b487455 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py @@ -92,6 +92,24 @@ class Beamlet(opcua_device): FPGA_beamlet_output_scale_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_scale_R"], datatype=numpy.double, dims=(16,)) FPGA_beamlet_output_scale_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_scale_RW"], datatype=numpy.double, dims=(16,), access=AttrWriteType.READ_WRITE) + # List of OPC-UA CP for BF beamlets + S_pn = SDP.S_pn + N_pn = SDP.N_pn + A_pn = 6 + N_pol = 2 + N_beamlets_ctrl = 488 + N_pol_bf = 2 + + FPGA_beamlet_output_nof_packets_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_nof_packets_R"], datatype=numpy.int32, dims=(N_beamlets_ctrl, N_pn)) + FPGA_beamlet_output_nof_valid_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_output_nof_valid_R"], datatype=numpy.int32, dims=(N_beamlets_ctrl, N_pn)) + + # uint16[N_pn][A_pn][N_pol][N_beamlets_ctrl] + # Select subband per dual-polarisation beamlet. + # 0 for antenna polarization X in beamlet polarization X, + # 1 for antenna polarization Y in beamlet polarization Y. + FPGA_beamlet_subband_select_R = attribute_wrapper(comms_annotation=["FPGA_beamlet_subband_select_R"], datatype=numpy.uint16, dims=(A_pn * N_pol * N_beamlets_ctrl, N_pn)) + FPGA_beamlet_subband_select_RW = attribute_wrapper(comms_annotation=["FPGA_beamlet_subband_select_RW"], datatype=numpy.uint16, dims=(A_pn * N_pol * N_beamlets_ctrl, N_pn), access=AttrWriteType.READ_WRITE) + # cint16[N_pn][A_pn][N_pol][N_beamlets_ctrl] # Co-polarization BF weights. The N_pol = 2 parameter index is: # 0 for antenna polarization X in beamlet polarization X, diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py index c4dc14a165f69ce471e7c416fef14dd0771b54cb..44e89a6cb2c8f3c1c1ddba17a2b94e3e34236e7e 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py @@ -108,6 +108,14 @@ class SDP(opcua_device): FPGA_pps_expected_cnt_RW = attribute_wrapper(comms_annotation=["FPGA_pps_expected_cnt_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_processing_enable_R = attribute_wrapper(comms_annotation=["FPGA_processing_enable_R"], datatype=numpy.bool_, dims=(16,)) FPGA_processing_enable_RW = attribute_wrapper(comms_annotation=["FPGA_processing_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_ring_node_offset_R = attribute_wrapper(comms_annotation=["FPGA_ring_node_offset_R"], datatype=numpy.uint32, dims=(16,)) + FPGA_ring_node_offset_RW = attribute_wrapper(comms_annotation=["FPGA_ring_node_offset_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_ring_nof_nodes_R = attribute_wrapper(comms_annotation=["FPGA_ring_nof_nodes_R"], datatype=numpy.uint32, dims=(16,)) + FPGA_ring_nof_nodes_RW = attribute_wrapper(comms_annotation=["FPGA_ring_nof_nodes_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_ring_use_cable_to_next_rn_R = attribute_wrapper(comms_annotation=["FPGA_ring_use_cable_to_next_rn_R"], datatype=numpy.bool_, dims=(16,)) + FPGA_ring_use_cable_to_next_rn_RW = attribute_wrapper(comms_annotation=["FPGA_ring_use_cable_to_next_rn_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_ring_use_cable_to_previous_rn_R = attribute_wrapper(comms_annotation=["FPGA_ring_use_cable_to_previous_rn_R"], datatype=numpy.bool_, dims=(16,)) + FPGA_ring_use_cable_to_previous_rn_RW = attribute_wrapper(comms_annotation=["FPGA_ring_use_cable_to_previous_rn_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_scrap_R = attribute_wrapper(comms_annotation=["FPGA_scrap_R"], datatype=numpy.int32, dims=(8192,)) FPGA_scrap_RW = attribute_wrapper(comms_annotation=["FPGA_scrap_RW"], datatype=numpy.int32, dims=(8192,), access=AttrWriteType.READ_WRITE) FPGA_sdp_info_antenna_band_index_R = attribute_wrapper(comms_annotation=["FPGA_sdp_info_antenna_band_index_R"], datatype=numpy.uint32, dims=(16,)) @@ -122,7 +130,7 @@ class SDP(opcua_device): FPGA_sdp_info_station_id_RW = attribute_wrapper(comms_annotation=["FPGA_sdp_info_station_id_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_subband_weights_R = attribute_wrapper(comms_annotation=["FPGA_subband_weights_R"], datatype=numpy.uint32, dims=(12 * 512, 16)) FPGA_subband_weights_RW = attribute_wrapper(comms_annotation=["FPGA_subband_weights_RW"], datatype=numpy.uint32, dims=(12 * 512, 16), access=AttrWriteType.READ_WRITE) - FPGA_time_since_last_pps_R = attribute_wrapper(comms_annotation=["FPGA_time_since_last_pps_R"], datatype=numpy.uint32, dims=(16,)) + FPGA_time_since_last_pps_R = attribute_wrapper(comms_annotation=["FPGA_time_since_last_pps_R"], datatype=numpy.float_, dims=(16,)) FPGA_temp_R = attribute_wrapper(comms_annotation=["FPGA_temp_R"], datatype=numpy.float_, dims=(16,)) FPGA_wg_amplitude_R = attribute_wrapper(comms_annotation=["FPGA_wg_amplitude_R"], datatype=numpy.float_, dims=(12, 16)) FPGA_wg_amplitude_RW = attribute_wrapper(comms_annotation=["FPGA_wg_amplitude_RW"], datatype=numpy.float_, dims=(12, 16), access=AttrWriteType.READ_WRITE) @@ -156,10 +164,10 @@ class SDP(opcua_device): FPGA_jesd204b_rx_err0_R = attribute_wrapper(comms_annotation=["FPGA_jesd204b_rx_err0_R"], datatype=numpy.uint32, dims=(S_pn, N_pn)) FPGA_jesd204b_rx_err1_R = attribute_wrapper(comms_annotation=["FPGA_jesd204b_rx_err1_R"], datatype=numpy.uint32, dims=(S_pn, N_pn)) - FPGA_bsn_monitor_input_bsn_R = attribute_wrapper(comms_annotation=["FPGA_bsn_monitor_input_bsn_R"], datatype=numpy.int64, dims=(N_pn,)) - FPGA_bsn_monitor_input_nof_packets_R = attribute_wrapper(comms_annotation=["FPGA_bsn_monitor_input_nof_packets_R"], datatype=numpy.int32, dims=(N_pn,)) - FPGA_bsn_monitor_input_nof_valid_R = attribute_wrapper(comms_annotation=["FPGA_bsn_monitor_input_nof_valid_R"], datatype=numpy.int32, dims=(N_pn,)) - FPGA_bsn_monitor_input_nof_err_R = attribute_wrapper(comms_annotation=["FPGA_bsn_monitor_input_nof_err_R"], datatype=numpy.int32, dims=(N_pn,)) + FPGA_signal_input_bsn_R = attribute_wrapper(comms_annotation=["FPGA_signal_input_bsn_R"], datatype=numpy.int64, dims=(N_pn,)) + FPGA_signal_input_nof_blocks_R = attribute_wrapper(comms_annotation=["FPGA_signal_input_nof_blocks_R"], datatype=numpy.int32, dims=(N_pn,)) + FPGA_signal_input_nof_samples_R = attribute_wrapper(comms_annotation=["FPGA_signal_input_nof_samples_R"], datatype=numpy.int32, dims=(N_pn,)) + FPGA_signal_input_nof_err_R = attribute_wrapper(comms_annotation=["FPGA_signal_input_nof_err_R"], datatype=numpy.int32, dims=(N_pn,)) FPGA_signal_input_samples_delay_R = attribute_wrapper(comms_annotation=["FPGA_signal_input_samples_delay_R"], datatype=numpy.uint32, dims=(S_pn, N_pn)) FPGA_signal_input_samples_delay_RW = attribute_wrapper(comms_annotation=["FPGA_signal_input_samples_delay_RW"], datatype=numpy.uint32, dims=(S_pn, N_pn), access=AttrWriteType.READ_WRITE) @@ -194,17 +202,14 @@ class SDP(opcua_device): # -------- def _prepare_hardware(self): - # FPGA firmware loading disabled, as it causes SDPTR to crash, - # see https://support.astron.nl/jira/browse/L2SDP-670 - """ - # FPGAs need the correct firmware loaded - self.FPGA_boot_image_RW = [1] * self.N_pn - - # wait for the firmware to be loaded (ignoring masked out elements) - mask = self.proxy.TR_fpga_mask_RW - self.wait_attribute("FPGA_boot_image_R", lambda attr: ((attr == 1) | ~mask).all(), 10) - """ - pass + # FPGAs that are actually reachable and we care about + wait_for = ~(self.read_attribute("TR_fpga_communication_error_R")) & self.read_attribute("TR_fpga_mask_R") + + # Order the correct firmare to be loaded + self.proxy.FPGA_boot_image_RW = [1] * self.N_pn + + # Wait for the firmware to be loaded (ignoring masked out elements) + self.wait_attribute("FPGA_boot_image_R", lambda attr: ((attr == 1) | ~wait_for).all(), 60) # -------- # Commands diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py index c7e9c95844b93768caa3622488ea2520244b38d9..1228cdc99d6ab8037f7c8bfe4ec859b5185cdd9e 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py @@ -90,6 +90,9 @@ class SST(Statistics): FPGA_sst_offload_weighted_subbands_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_sst_offload_weighted_subbands_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_sst_offload_weighted_subbands_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_sst_offload_weighted_subbands_R"], datatype=numpy.bool_, dims=(16,)) + FPGA_sst_offload_nof_packets_R = attribute_wrapper(comms_annotation=["FPGA_sst_offload_nof_packets_R"], datatype=numpy.int32, dims=(16,)) + FPGA_sst_offload_nof_valid_R = attribute_wrapper(comms_annotation=["FPGA_sst_offload_nof_valid_R"], datatype=numpy.int32, dims=(16,)) + # number of packets with valid payloads nof_valid_payloads_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_valid_payloads"}, dims=(SSTCollector.MAX_FPGAS,), datatype=numpy.uint64) # number of packets with invalid payloads diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py index 8be9cdb483ef4f21818791009a19e8fbc2b91cb9..73a5d85bb742f06bf5412926cadefe4db0455716 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py @@ -110,6 +110,11 @@ class XST(Statistics): FPGA_xst_offload_nof_crosslets_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_xst_offload_nof_crosslets_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_xst_offload_nof_crosslets_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_xst_offload_nof_crosslets_R"], datatype=numpy.uint32, dims=(16,)) + FPGA_xst_ring_nof_transport_hops_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_xst_ring_nof_transport_hops_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_xst_ring_nof_transport_hops_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_xst_ring_nof_transport_hops_R"], datatype=numpy.uint32, dims=(16,)) + + FPGA_xst_offload_nof_packets_R = attribute_wrapper(comms_annotation=["FPGA_xst_offload_nof_packets_R"], datatype=numpy.int32, dims=(16,)) + FPGA_xst_offload_nof_valid_R = attribute_wrapper(comms_annotation=["FPGA_xst_offload_nof_valid_R"], datatype=numpy.int32, dims=(16,)) # number of packets with valid payloads nof_valid_payloads_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_valid_payloads"}, dims=(XSTCollector.MAX_FPGAS,), datatype=numpy.uint64) diff --git a/tangostationcontrol/tangostationcontrol/test/devices/test_recv_device.py b/tangostationcontrol/tangostationcontrol/test/devices/test_recv_device.py index 1fde8856cf55a79a03aaec2cc53ba34a4ad818f0..49de70bd5aecc1faab3bf7402f7adad059bd3823 100644 --- a/tangostationcontrol/tangostationcontrol/test/devices/test_recv_device.py +++ b/tangostationcontrol/tangostationcontrol/test/devices/test_recv_device.py @@ -19,7 +19,8 @@ from tangostationcontrol.test import base class TestRecvDevice(base.TestCase): # some dummy values for mandatory properties - recv_properties = {'OPC_Server_Name': 'example.com', 'OPC_Server_Port': 4840, 'OPC_Time_Out': 5.0} + recv_properties = {'OPC_Server_Name': 'example.com', 'OPC_Server_Port': 4840, 'OPC_Time_Out': 5.0, + 'Antenna_Field_Reference_ITRF' : [3.0, 3.0, 3.0], 'Antenna_Field_Reference_ETRS' : [7.0, 7.0, 7.0]} def setUp(self): super(TestRecvDevice, self).setUp() @@ -31,11 +32,20 @@ class TestRecvDevice(base.TestCase): device, 'DeviceProxy') proxy_patcher.start() self.addCleanup(proxy_patcher.stop) - - + def test_calculate_HBAT_bf_delay_steps(self): """Verify HBAT beamforming calculations are correctly executed""" with DeviceTestContext(recv.RECV, properties=self.recv_properties, process=True) as proxy: delays = numpy.random.rand(96,16).flatten() HBAT_bf_delay_steps = proxy.calculate_HBAT_bf_delay_steps(delays) self.assertEqual(3072, len(HBAT_bf_delay_steps)) # 96x32=3072 + + def test_read_Antenna_Field_Reference(self): + """Verify if Antenna coordinates are correctly provided""" + # Device uses ITRF coordinates by default + with DeviceTestContext(recv.RECV, properties=self.recv_properties, process=True) as proxy: + self.assertEqual(3.0, proxy.Antenna_Field_Reference_ITRF_R[0]) + # Device derives coordinates from ETRS if ITRF ones are not found + recv_properties_v2 = {'OPC_Server_Name': 'example.com', 'OPC_Server_Port': 4840, 'OPC_Time_Out': 5.0, 'Antenna_Field_Reference_ETRS' : [7.0, 7.0, 7.0]} + with DeviceTestContext(recv.RECV, properties=recv_properties_v2, process=True) as proxy: + self.assertNotEqual(3.0, proxy.Antenna_Field_Reference_ITRF_R[0]) # value = 6.948998835785814