diff --git a/tangostationcontrol/tangostationcontrol/devices/calibration.py b/tangostationcontrol/tangostationcontrol/devices/calibration.py index 7de1215650323a9aac188a6275af8806dba909ac..b295a47bce3be34d6d3ae170135e02c93385cd97 100644 --- a/tangostationcontrol/tangostationcontrol/devices/calibration.py +++ b/tangostationcontrol/tangostationcontrol/devices/calibration.py @@ -4,7 +4,9 @@ """ Calibration Device Server for LOFAR2.0 """ +import datetime import logging +import numpy from tango import EventType, Database from tango.server import device_property, command, attribute @@ -42,13 +44,30 @@ class Calibration(LOFARDevice): self.sdpfirmware_proxies: dict = {} self.sdp_proxies: dict = {} self.ant_proxies: dict = {} + self.last_ant_calibration_timestamp: dict[str, datetime.datetime | None] = {} # Super must be called after variable assignment due to executing init_device! super().__init__(cl, name) + def _calibrate_antenna_field(self, ant_proxy): + """Recalibrate a specific AntennaField.""" + + if ant_proxy.state() not in DEFAULT_COMMAND_STATES: + logger.warning( + f"Device {ant_proxy.name()} not active. Forgoing calibration." + ) + return + + logger.info("Re-calibrate antenna field %s", ant_proxy.name()) + + self.last_ant_calibration_timestamp[ant_proxy] = datetime.datetime.now() + + self.calibrate_recv(ant_proxy) + self.calibrate_sdp(ant_proxy) + @log_exceptions() - def _frequency_band_changed_event(self, event): - """Trigger on external changes in frequency settings.""" + def _antennafield_changed_event(self, event): + """Trigger on key external changes in AntennaField settings.""" if event.err: # little we can do here. note that errors are also @@ -63,13 +82,11 @@ class Calibration(LOFARDevice): ) if self.dev_state() not in DEFAULT_COMMAND_STATES: - logger.warning("Device not active. Ignore frequency band changed event") + logger.warning("Device not active. Ignore AntennaField changed event") return # frequencies changed, so we need to recalibrate - logger.info("Re-calibrate antenna field %s", event.device.name()) - self.calibrate_recv(event.device) - self.calibrate_sdp(event.device) + self._calibrate_antenna_field(event.device) @log_exceptions() def _clock_changed_event(self, event): @@ -91,12 +108,12 @@ class Calibration(LOFARDevice): return for k, ant in self.ant_proxies.items(): + # Recalibrate associated AntennaFields sdpfirmware_device = ant.SDPFirmware_device_R.casefold() sdp_device = self.sdpfirmware_proxies[sdpfirmware_device].SDP_device_R + if device_name_matches(sdp_device, event.device.name()): - logger.info("Re-calibrate antenna field %s", k) - self.calibrate_sdp(k) - self.calibrate_recv(k) + self._calibrate_antenna_field(k) # TODO(JDM): While we could read this from our control parent (StationManager), # doing so creates a deadlock when StationManager wants to initialise this @@ -121,16 +138,29 @@ class Calibration(LOFARDevice): def AntennaFields_Monitored_R(self): return list(self.ant_proxies.keys()) + @attribute(dtype=(numpy.int64,), max_dim_x=20) + def Last_AntennaField_Calibration_Timestamp_R(self): + return numpy.array( + [ + ts.timestamp() if ts else 0 + for ts in self.last_ant_calibration_timestamp.values() + ], + dtype=numpy.int64, + ) + @attribute(dtype=(str,), max_dim_x=20) def SDPs_Monitored_R(self): return list(self.sdp_proxies.keys()) - @command + @command() + @only_in_states(DEFAULT_COMMAND_STATES) def download_calibration_tables(self): + """Download the latest calibration tables and apply them.""" + self._calibration_manager.sync_calibration_tables() - for ant in self.ant_proxies.keys(): - self.calibrate_recv(ant) - self.calibrate_sdp(ant) + + # Apply downloaded tables + self.calibrate_all() @command(dtype_in=str) @only_in_states(DEFAULT_COMMAND_STATES) @@ -200,8 +230,7 @@ class Calibration(LOFARDevice): logger.info("Re-calibrate antenna field %s", k) try: - self.calibrate_sdp(k) - self.calibrate_recv(k) + self._calibrate_antenna_field(k) except Exception as exc: logger.exception("Could not calibrate antenna field %s", k) @@ -221,6 +250,7 @@ class Calibration(LOFARDevice): for d in devices: logger.debug("found antenna field device %s", str(d)) self.ant_proxies = {d.casefold(): create_device_proxy(d) for d in devices} + self.last_ant_calibration_timestamp = {d.casefold(): None for d in devices} devices = db.get_device_exported_for_class(SDPFirmware.__name__) for d in devices: @@ -243,11 +273,24 @@ class Calibration(LOFARDevice): prx.subscribe_event( "Frequency_Band_RW", EventType.CHANGE_EVENT, - self._frequency_band_changed_event, + self._antennafield_changed_event, stateless=True, ), ) ) + + self.event_subscriptions.append( + ( + prx, + prx.subscribe_event( + "State", + EventType.CHANGE_EVENT, + self._antennafield_changed_event, + stateless=True, + ), + ) + ) + for prx in self.sdpfirmware_proxies.values(): self.event_subscriptions.append( (