From 29e69aa7a1919f791153ca39e47b80c4a7abdb27 Mon Sep 17 00:00:00 2001
From: Jan David Mol <mol@astron.nl>
Date: Tue, 12 Sep 2023 17:01:39 +0000
Subject: [PATCH] L2SS-1501: Fixes from busy day 2012-09-07

---
 CDB/stations/testenv_cs001.json               | 15 ++++++--
 .../tangostationcontrol/common/constants.py   |  2 +-
 .../devices/antennafield.py                   |  8 +++--
 .../tangostationcontrol/devices/apsct.py      | 12 ++++---
 .../base_device_classes/hierarchy_device.py   |  2 +-
 .../base_device_classes/lofar_device.py       | 34 +++++++++++++++----
 .../devices/sdp/firmware.py                   | 25 +++++++++++---
 7 files changed, 76 insertions(+), 22 deletions(-)

diff --git a/CDB/stations/testenv_cs001.json b/CDB/stations/testenv_cs001.json
index 0cfb260ea..6bf8d12a9 100644
--- a/CDB/stations/testenv_cs001.json
+++ b/CDB/stations/testenv_cs001.json
@@ -436,7 +436,7 @@
                             "RCU_On_Off_timeout": [
                                 "1"
                             ],
-                              "RCU_DTH_On_Off_timeout": [
+                            "RCU_DTH_On_Off_timeout": [
                                 "1"
                             ]
                         }
@@ -461,7 +461,7 @@
                             "RCU_On_Off_timeout": [
                                 "1"
                             ],
-                              "RCU_DTH_On_Off_timeout": [
+                            "RCU_DTH_On_Off_timeout": [
                                 "1"
                             ]
                         }
@@ -480,7 +480,7 @@
                             "RCU_On_Off_timeout": [
                                 "1"
                             ],
-                              "RCU_DTH_On_Off_timeout": [
+                            "RCU_DTH_On_Off_timeout": [
                                 "1"
                             ]
                         }
@@ -501,6 +501,9 @@
                             ],
                             "OPC_Time_Out": [
                                 "5.0"
+                            ],
+                            "Firmware_Boot_timeout": [
+                                "1.0"
                             ]
                         }
                     },
@@ -514,6 +517,9 @@
                             ],
                             "OPC_Time_Out": [
                                 "5.0"
+                            ],
+                            "Firmware_Boot_timeout": [
+                                "1.0"
                             ]
                         }
                     },
@@ -527,6 +533,9 @@
                             ],
                             "OPC_Time_Out": [
                                 "5.0"
+                            ],
+                            "Firmware_Boot_timeout": [
+                                "1.0"
                             ]
                         }
                     }
diff --git a/tangostationcontrol/tangostationcontrol/common/constants.py b/tangostationcontrol/tangostationcontrol/common/constants.py
index 3a0de82d0..39fe47963 100644
--- a/tangostationcontrol/tangostationcontrol/common/constants.py
+++ b/tangostationcontrol/tangostationcontrol/common/constants.py
@@ -84,7 +84,7 @@ N_point_prop = 3
 N_latlong = 2
 
 # default subband we use because of its low RFI
-DEFAULT_SUBBAND = 102
+DEFAULT_SUBBAND = 300
 
 # Maximum array size to allocate for beam_device pointings,
 MAX_POINTINGS = N_beamlets_max
diff --git a/tangostationcontrol/tangostationcontrol/devices/antennafield.py b/tangostationcontrol/tangostationcontrol/devices/antennafield.py
index 6b4d732c6..4c1cb0780 100644
--- a/tangostationcontrol/tangostationcontrol/devices/antennafield.py
+++ b/tangostationcontrol/tangostationcontrol/devices/antennafield.py
@@ -1083,8 +1083,12 @@ class AntennaField(LOFARDevice):
         # Enable controlling all antennas
         self.proxy.write_attribute("ANT_mask_RW", [True] * len(ANT_mask_RW))
 
-        # Turn off power to all antennas
-        self.proxy.write_attribute("RCU_PWR_ANT_on_RW", [True] * len(ANT_mask_RW))
+        try:
+            # Turn off power to all antennas
+            self.proxy.write_attribute("RCU_PWR_ANT_on_RW", [False] * len(ANT_mask_RW))
+        finally:
+            # Restore original mask
+            self.proxy.write_attribute("ANT_mask_RW", ANT_mask_RW)
 
     # --------
     # Commands
diff --git a/tangostationcontrol/tangostationcontrol/devices/apsct.py b/tangostationcontrol/tangostationcontrol/devices/apsct.py
index 37747940d..9b7023b1c 100644
--- a/tangostationcontrol/tangostationcontrol/devices/apsct.py
+++ b/tangostationcontrol/tangostationcontrol/devices/apsct.py
@@ -198,11 +198,7 @@ class APSCT(OPCUADevice):
     def _power_hardware_on(self):
         """Turns on the 200MHz clock."""
 
-        # Cycle clock
-        self.APSCT_off()
-        self.wait_attribute(
-            "APSCTTR_translator_busy_R", False, self.APSCT_On_Off_timeout
-        )
+        # Turn on 200 MHz clock
         self.APSCT_200MHz_on()
         self.wait_attribute(
             "APSCTTR_translator_busy_R", False, self.APSCT_On_Off_timeout
@@ -242,6 +238,11 @@ class APSCT(OPCUADevice):
     @only_in_states(DEFAULT_COMMAND_STATES)
     def APSCT_off(self):
         """
+        Turn off the clock.
+
+        Warning: This stops the user image in SDP, making
+                 it unresponsive. The factory image is
+                 not affected.
 
         :return:None
         """
@@ -252,6 +253,7 @@ class APSCT(OPCUADevice):
     @only_in_states(DEFAULT_COMMAND_STATES)
     def APSCT_200MHz_on(self):
         """
+        Set the clock to 200 MHz.
 
         :return:None
         """
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/hierarchy_device.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/hierarchy_device.py
index ece897ae6..364ee39f0 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/hierarchy_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/hierarchy_device.py
@@ -163,7 +163,7 @@ class AbstractHierarchyDevice:
         """
 
         if not self._proxies.get(device):
-            self._proxies[device] = create_device_proxy(device, 30000)
+            self._proxies[device] = create_device_proxy(device, 60_000)
 
         return self._proxies[device]
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py
index 9cff03eaf..116cd4299 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py
@@ -17,7 +17,6 @@ from attribute_wrapper.attribute_wrapper import AttributeWrapper
 from tango import (
     AttrDataFormat,
     Attribute,
-    AttrWriteType,
     DebugIt,
     DeviceProxy,
     DevState,
@@ -88,12 +87,24 @@ class LOFARDevice(Device):
     # ----------
 
     version_R = attribute(
-        dtype=str, access=AttrWriteType.READ, fget=lambda self: version
+        doc="Version of this software device", dtype=str, fget=lambda self: version
+    )
+
+    uptime_R = attribute(
+        doc="How long this software device has been running (wall-clock time, in seconds)",
+        unit="s",
+        dtype=numpy.int64,
+        fget=lambda self: numpy.int64(time.time() - self._device_start_time),
+    )
+
+    access_count_R = attribute(
+        doc="How often this software device was accessed for commands or attributes",
+        dtype=numpy.int64,
+        fget=lambda self: numpy.int64(self._access_count),
     )
 
     hardware_powered_R = attribute(
         dtype=bool,
-        access=AttrWriteType.READ,
         fget=lambda self: self._read_hardware_powered_R(),
     )
 
@@ -229,6 +240,15 @@ class LOFARDevice(Device):
             elif not mask[idx]:
                 merge_values[idx] = current_values[idx]
 
+    def __init__(self, cl, name):
+        super().__init__(cl, name)
+
+        # record how many time this device was accessed
+        self._access_count = 0
+
+        # record when this device was started
+        self._device_start_time = time.time()
+
     def _init_device(self):
         logger.debug("[LOFARDevice] init_device")
 
@@ -424,8 +444,10 @@ class LOFARDevice(Device):
         pass
 
     def always_executed_hook(self):
-        """Method always executed before any TANGO command is executed."""
-        pass
+        """Method always executed before any TANGO command is executed or attribute is accessed."""
+
+        # increase the number of accesses
+        self._access_count += 1
 
     def _read_hardware_powered_R(self):
         """Overloadable function called in read attribute hardware_powered_R"""
@@ -519,6 +541,7 @@ class LOFARDevice(Device):
 
         self._set_defaults_for(self.TRANSLATOR_DEFAULT_SETTINGS)
 
+    @only_in_states(INITIALISED_STATES)
     @command()
     @DebugIt()
     @log_exceptions()
@@ -529,7 +552,6 @@ class LOFARDevice(Device):
         self._power_hardware_on()
 
     @only_in_states(INITIALISED_STATES)
-    @fault_on_error()
     @command()
     @DebugIt()
     @log_exceptions()
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py b/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
index 85874d37d..866529f48 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
@@ -49,6 +49,15 @@ class SDPFirmware(OPCUADevice):
         dtype="DevULong", mandatory=False, default_value=CLK_200_MHZ
     )
 
+    # ----- Timing values
+
+    Firmware_Boot_timeout = device_property(
+        doc="Maximum amount of time to wait for FPGAs to boot.",
+        dtype="DevFloat",
+        mandatory=False,
+        default_value=60.0,
+    )
+
     TRANSLATOR_DEFAULT_SETTINGS = ["TR_fpga_mask_RW"]
 
     # ----------
@@ -264,16 +273,24 @@ class SDPFirmware(OPCUADevice):
 
     def _boot_to_image(self, image_nr):
         # 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")
+        wait_for = self.read_attribute("TR_fpga_mask_R")
+
+        # Wait for the current image to be booted (in case we connected to SDPTR before even the current
+        # image finished booting).
+        self.wait_attribute(
+            "TR_fpga_communication_error_R",
+            lambda attr: (~attr | ~wait_for).all(),
+            self.Firmware_Boot_timeout,
+        )
 
         # Order the correct firmare to be loaded
         self.proxy.FPGA_boot_image_RW = [image_nr] * 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
+            "FPGA_boot_image_R",
+            lambda attr: ((attr == image_nr) | ~wait_for).all(),
+            self.Firmware_Boot_timeout,
         )
 
     def _power_hardware_on(self):
-- 
GitLab