From 5e2bff8a3f890e4774bf0772922402cd50beb1e9 Mon Sep 17 00:00:00 2001
From: Jan David Mol <mol@astron.nl>
Date: Wed, 21 Feb 2024 14:41:53 +0000
Subject: [PATCH] Rollout fixes v0.27.1/v0.28

---
 README.md                                     |  1 +
 docker/grafana/stationcontrol-dashboards.yaml | 25 ++++++++++++--
 tangostationcontrol/VERSION                   |  2 +-
 .../base_device_classes/power_hierarchy.py    |  4 +--
 .../devices/calibration.py                    | 12 +++++++
 .../tangostationcontrol/devices/ec.py         |  6 +---
 .../devices/observation_control.py            |  6 ++--
 .../devices/sdp/beamlet.py                    | 33 +++++++++++++++----
 .../devices/sdp/firmware.py                   |  5 ++-
 9 files changed, 72 insertions(+), 22 deletions(-)

diff --git a/README.md b/README.md
index 1c047091c..b3c2deb1c 100644
--- a/README.md
+++ b/README.md
@@ -166,6 +166,7 @@ Next change the version in the following places:
 
 # Release Notes
 
+* 0.28.1 Bugfixes / rollout fixes
 * 0.28.0 Make `StationManager` device asynchronous
 * 0.27.2 Add new attributes in OPCUA devices
 * 0.27.1 Bugfixes / rollout fixes
diff --git a/docker/grafana/stationcontrol-dashboards.yaml b/docker/grafana/stationcontrol-dashboards.yaml
index 50d300483..1933efa7f 100644
--- a/docker/grafana/stationcontrol-dashboards.yaml
+++ b/docker/grafana/stationcontrol-dashboards.yaml
@@ -2,7 +2,7 @@ apiVersion: 1
 
 providers:
   # <string> an unique provider name. Required
-  - name: 'StationControl'
+  - name: 'StationControl (panels)'
     # <int> Org id. Default to 1
     orgId: 1
     # <string> name of the dashboard folder.
@@ -16,7 +16,28 @@ providers:
     # <int> how often Grafana will scan for changed dashboards
     updateIntervalSeconds: 60
     # <bool> allow updating provisioned dashboards from the UI
-    allowUiUpdates: false
+    allowUiUpdates: true
+    options:
+      # <string, required> path to dashboard files on disk. Required when using the 'file' type
+      path: /var/lib/grafana/panels
+      # <bool> use folder names from filesystem to create folders in Grafana
+      foldersFromFilesStructure: true
+  # <string> an unique provider name. Required
+  - name: 'StationControl (dashboards)'
+    # <int> Org id. Default to 1
+    orgId: 1
+    # <string> name of the dashboard folder.
+    folder: ''
+    # <string> folder UID. will be automatically generated if not specified
+    folderUid: ''
+    # <string> provider type. Default to 'file'
+    type: file
+    # <bool> disable dashboard deletion
+    disableDeletion: true
+    # <int> how often Grafana will scan for changed dashboards
+    updateIntervalSeconds: 60
+    # <bool> allow updating provisioned dashboards from the UI
+    allowUiUpdates: true
     options:
       # <string, required> path to dashboard files on disk. Required when using the 'file' type
       path: /var/lib/grafana/dashboards
diff --git a/tangostationcontrol/VERSION b/tangostationcontrol/VERSION
index 697f087f3..48f7a71df 100644
--- a/tangostationcontrol/VERSION
+++ b/tangostationcontrol/VERSION
@@ -1 +1 @@
-0.28.0
+0.28.1
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/power_hierarchy.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/power_hierarchy.py
index f710c2524..efebe69bb 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/power_hierarchy.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/power_hierarchy.py
@@ -31,8 +31,8 @@ class PowerHierarchyDevice(AbstractHierarchyDevice):
     POWER_CHILD_PROPERTY = "Power_Children"
 
     HIBERNATE_TIMEOUT = 60.0
-    STANDBY_TIMEOUT = 60.0
-    ON_TIMEOUT = 60.0
+    STANDBY_TIMEOUT = 300.0
+    ON_TIMEOUT = 300.0
 
     def init(
         self,
diff --git a/tangostationcontrol/tangostationcontrol/devices/calibration.py b/tangostationcontrol/tangostationcontrol/devices/calibration.py
index 7ca47bfc6..ff3cb3280 100644
--- a/tangostationcontrol/tangostationcontrol/devices/calibration.py
+++ b/tangostationcontrol/tangostationcontrol/devices/calibration.py
@@ -199,6 +199,7 @@ class Calibration(LOFARDevice):
 
         Run whenever the following changes:
             sdpfirmware.clock_RW
+            sdpfirmware.FPGA_boot_image_R
             antennafield.RCU_band_select_RW
         """
 
@@ -317,6 +318,17 @@ class Calibration(LOFARDevice):
                     ),
                 )
             )
+            self.event_subscriptions.append(
+                (
+                    prx,
+                    prx.subscribe_event(
+                        "FPGA_boot_image_R",
+                        EventType.CHANGE_EVENT,
+                        self._clock_changed_event,
+                        stateless=True,
+                    ),
+                )
+            )
 
     def configure_for_on(self):
         # Calibrate all antennafields, as we did not receive
diff --git a/tangostationcontrol/tangostationcontrol/devices/ec.py b/tangostationcontrol/tangostationcontrol/devices/ec.py
index 78ce80e02..3bc7354e2 100644
--- a/tangostationcontrol/tangostationcontrol/devices/ec.py
+++ b/tangostationcontrol/tangostationcontrol/devices/ec.py
@@ -24,11 +24,7 @@ __all__ = ["EC"]
 
 
 @device_logging_to_python()
-@device_metrics(
-    exclude=[
-        "*_RW",
-    ]
-)
+@device_metrics()
 class EC(OPCUADevice):
     """EC Device Server for LOFAR2.0"""
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/observation_control.py b/tangostationcontrol/tangostationcontrol/devices/observation_control.py
index ca21bfc15..c57453e69 100644
--- a/tangostationcontrol/tangostationcontrol/devices/observation_control.py
+++ b/tangostationcontrol/tangostationcontrol/devices/observation_control.py
@@ -5,13 +5,12 @@ import logging
 
 import numpy
 from tango import (
-    DevState,
     DebugIt,
     Util,
     DevBoolean,
     DevString,
 )
-from tango.server import Device, command, attribute
+from tango.server import command, attribute
 from tangostationcontrol.observation.observation_controller import ObservationController
 from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
@@ -122,8 +121,7 @@ class ObservationControl(LOFARDevice):
     @DebugIt()
     def init_device(self):
         logger.debug("[ObservationControl] init device")
-        Device.init_device(self)
-        self.set_state(DevState.OFF)
+        super().init_device()
 
         # Increase the number of polling threads for this device server.
         # NB: This server hosts both ObservationControl and Observation
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py
index dd1e99c06..5f2fb24a6 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py
@@ -224,12 +224,20 @@ class Beamlet(OPCUADevice):
         datatype=numpy.uint8,
         dims=(N_pn,),
     )
-    FPGA_beamlet_output_nof_destinations_RW = AttributeWrapper(
+    _FPGA_beamlet_output_nof_destinations_RW = AttributeWrapper(
+        doc="Number of UDP streams to create. NB: sdp.FPGA_processing_enable_R must be all False when setting this value! Use beamlet_output_nof_destinations_RW instead!",
         comms_annotation=["FPGA_beamlet_output_nof_destinations_RW"],
         datatype=numpy.uint8,
         dims=(N_pn,),
         access=AttrWriteType.READ_WRITE,
     )
+    FPGA_beamlet_output_nof_destinations_RW = attribute(
+        doc="Number of UDP streams to create.",
+        dtype=(numpy.uint8,),
+        max_dim_x=N_pn,
+        access=AttrWriteType.READ_WRITE,
+    )
+
     FPGA_beamlet_output_nof_destinations_act_R = AttributeWrapper(
         comms_annotation=["FPGA_beamlet_output_nof_destinations_act_R"],
         datatype=numpy.uint8,
@@ -668,6 +676,23 @@ class Beamlet(OPCUADevice):
 
         return default_settings
 
+    def read_FPGA_beamlet_output_nof_destinations_RW(self):
+        # report effective number of output destinations
+        return self.read_attribute("FPGA_beamlet_output_nof_destinations_act_R")
+
+    def write_FPGA_beamlet_output_nof_destinations_RW(self, value):
+        old_FPGA_processing_enable = self.sdp_proxy.FPGA_processing_enable_RW
+        try:
+            # need to turn off FPGA processing when setting this value
+            self.sdp_proxy.FPGA_processing_enable_RW = [False] * N_pn
+
+            self.proxy.write_attribute(
+                "_FPGA_beamlet_output_nof_destinations_RW", value
+            )
+        finally:
+            # restore previous setting
+            self.sdp_proxy.FPGA_processing_enable_RW = old_FPGA_processing_enable
+
     def read_subband_select_RW(self):
         # We can only return a single value, so we assume the FPGA is configured coherently.
         # Which is something that is to be checked by an independent monitoring system anyway.
@@ -730,12 +755,6 @@ class Beamlet(OPCUADevice):
             )
         )
 
-    def configure_for_on(self):
-        super().configure_for_on()
-
-        # after everything is configured, FPGA processing can be turned on
-        self.sdp_proxy.FPGA_processing_enable_RW = [True] * N_pn
-
     def configure_for_off(self):
         super().configure_for_off()
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py b/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
index 9df30ad75..f024dab26 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
@@ -37,8 +37,9 @@ __all__ = ["SDPFirmware"]
 @device_logging_to_python()
 @device_metrics(
     exclude=[
-        "FPGA_boot_image_*",
         "FPGA_scrap_*",
+        "FPGA_flash_*",
+        "FPGA_ucp_*",
         "*_RW",
     ],
     include=[
@@ -131,6 +132,8 @@ class SDPFirmware(OPCUADevice):
         datatype=numpy.int32,
         dims=(N_pn,),
         doc="Active FPGA image (0=factory, 1=user)",
+        polling_period=DEFAULT_POLLING_PERIOD,
+        abs_change=1,
     )
     FPGA_boot_image_RW = AttributeWrapper(
         comms_annotation=["FPGA_boot_image_RW"],
-- 
GitLab