diff --git a/CDB/LOFAR_ConfigDb.json b/CDB/LOFAR_ConfigDb.json index 97de683bb7ebd6bc26b9e357812aaddd11b3d344..d5d1ad78ba3f14d3c64711f6e2b0d85a5b99e597 100644 --- a/CDB/LOFAR_ConfigDb.json +++ b/CDB/LOFAR_ConfigDb.json @@ -22,8 +22,7 @@ "STAT/Configuration/1", "STAT/ObservationControl/1", "STAT/TemperatureManager/1", - "STAT/Calibration/1", - "STAT/Boot/1" + "STAT/Calibration/1" ], "Control_Children": [ @@ -36,7 +35,6 @@ "STAT/TemperatureManager/1", "STAT/Calibration/1", "STAT/Configuration/1", - "STAT/Boot/1", "STAT/RECVL/1" ] } @@ -44,14 +42,6 @@ } } }, - "Boot": { - "STAT": { - "Boot": { - "STAT/Boot/1": { - } - } - } - }, "CCD": { "STAT": { "CCD": { diff --git a/CDB/stations/DTS.json b/CDB/stations/DTS.json index dc00315f489b8cfdd6b83c30e02aabc642568d45..e1fad56c4e9c09aec5ac325321fa75e2a03599c6 100644 --- a/CDB/stations/DTS.json +++ b/CDB/stations/DTS.json @@ -11,7 +11,6 @@ "STAT/Calibration/1", "STAT/ObservationControl/1", "STAT/TemperatureManager/1", - "STAT/Boot/1", "STAT/AntennaField/LBA", "STAT/AntennaField/HBA" ], @@ -24,7 +23,6 @@ "STAT/Calibration/1", "STAT/ObservationControl/1", "STAT/TemperatureManager/1", - "STAT/Boot/1", "STAT/AntennaField/LBA", "STAT/AntennaField/HBA", "STAT/APS/L0", @@ -70,50 +68,6 @@ } } }, - "boot": { - "STAT": { - "Boot": { - "STAT/Boot/1": { - "properties": { - "Device_Names": [ - "STAT/Configuration/1", - "STAT/PSOC/1", - "STAT/PCON/1", - "STAT/APSPU/L0", - "STAT/APSCT/L0", - "STAT/RECVL/L0", - "STAT/UNB2/L0", - "STAT/APSPU/L1", - "STAT/APSCT/L1", - "STAT/RECVL/L1", - "STAT/UNB2/L1", - "STAT/APSPU/H0", - "STAT/APSCT/H0", - "STAT/RECVH/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" - ] - } - } - } - } - }, "CCD": { "STAT": { "CCD": { diff --git a/CDB/stations/LTS_ConfigDb.json b/CDB/stations/LTS_ConfigDb.json index 65333660b47151ea7b6b86ea6c34937a7cc47799..fd98d77501860cc79646c43a009e71371925e248 100644 --- a/CDB/stations/LTS_ConfigDb.json +++ b/CDB/stations/LTS_ConfigDb.json @@ -16,19 +16,6 @@ } } }, - "boot": { - "STAT": { - "Boot": { - "STAT/Boot/1": { - "properties": { - "Initialise_Hardware": [ - "True" - ] - } - } - } - } - }, "Beamlet": { "STAT": { "Beamlet": { diff --git a/CDB/stations/README.md b/CDB/stations/README.md index 7180538d09e73375c99e28723398bdbff5d313bf..1de4fa4d37d85d22dfd54769ceae042bdb73a0d7 100644 --- a/CDB/stations/README.md +++ b/CDB/stations/README.md @@ -21,7 +21,6 @@ These are the required changes that need to be made in order to support multiple ##### In the config file: - add all the new devices under the "STAT/DEVICE/2" name with the correct IP addresses, ports and other properties. This includes: APSCT, APSPU, RECV and UNB2. - - Put the new devices in the "Device_Names" list for the boot devices - Set the first 8 bools in "STAT/SDPFirmware/1" ... "TR_fpga_mask_RW_default" to True (instead of the first 4 with 1 subrack). This enables the new FPGA's ##### In the jupyter startup script diff --git a/CDB/stations/common.json b/CDB/stations/common.json index 45768cf244203c86e94ca786550ae669aaf30e96..652b10d7981d566a601f6f27a62e1e92aaff1b46 100644 --- a/CDB/stations/common.json +++ b/CDB/stations/common.json @@ -10,8 +10,7 @@ "STAT/Configuration/1", "STAT/Calibration/1", "STAT/ObservationControl/1", - "STAT/TemperatureManager/1", - "STAT/Boot/1" + "STAT/TemperatureManager/1" ], "Control_Children": [ "STAT/CCD/1", @@ -20,24 +19,13 @@ "STAT/Configuration/1", "STAT/Calibration/1", "STAT/ObservationControl/1", - "STAT/TemperatureManager/1", - "STAT/Boot/1" + "STAT/TemperatureManager/1" ] } } } } }, - "boot": { - "STAT": { - "Boot": { - "STAT/Boot/1": { - "properties": { - } - } - } - } - }, "Calibration": { "STAT": { "Calibration": { @@ -196,16 +184,6 @@ } } } - }, - "Boot": { - "STAT": { - "Boot": { - "STAT/Boot/1": { - "properties": { - } - } - } - } } } } diff --git a/CDB/stations/cs001.json b/CDB/stations/cs001.json index 36e7806652952e23883874399685b0b83d89d41a..f806983a1a2167184bc43436b5767d24dc2b49cb 100644 --- a/CDB/stations/cs001.json +++ b/CDB/stations/cs001.json @@ -8,7 +8,6 @@ "Power_Children": [ "STAT/EC/1", "STAT/TemperatureManager/1", - "STAT/Boot/1", "STAT/AntennaField/LBA", "STAT/AntennaField/HBA0", "STAT/AntennaField/HBA1", @@ -25,7 +24,6 @@ "STAT/Calibration/1", "STAT/ObservationControl/1", "STAT/TemperatureManager/1", - "STAT/Boot/1", "STAT/APS/L0", "STAT/APS/L1", "STAT/APS/H0", @@ -72,58 +70,6 @@ } } }, - "boot": { - "STAT": { - "Boot": { - "STAT/Boot/1": { - "properties": { - "Device_Names": [ - "STAT/Configuration/1", - "STAT/PSOC/1", - "STAT/PCON/1", - "STAT/APSPU/L0", - "STAT/APSCT/L0", - "STAT/RECVL/L0", - "STAT/UNB2/L0", - "STAT/APSPU/L1", - "STAT/APSCT/L1", - "STAT/RECVL/L1", - "STAT/UNB2/L1", - "STAT/APSPU/H0", - "STAT/APSCT/H0", - "STAT/RECVH/H0", - "STAT/UNB2/H0", - "STAT/CCD/1", - "STAT/SDP/LBA", - "STAT/BST/LBA", - "STAT/SST/LBA", - "STAT/XST/LBA", - "STAT/Beamlet/LBA", - "STAT/AntennaField/LBA", - "STAT/DigitalBeam/LBA", - "STAT/SDP/HBA0", - "STAT/BST/HBA0", - "STAT/SST/HBA0", - "STAT/XST/HBA0", - "STAT/Beamlet/HBA0", - "STAT/AntennaField/HBA0", - "STAT/TileBeam/HBA0", - "STAT/DigitalBeam/HBA0", - "STAT/SDP/HBA1", - "STAT/BST/HBA1", - "STAT/SST/HBA1", - "STAT/XST/HBA1", - "STAT/Beamlet/HBA1", - "STAT/AntennaField/HBA1", - "STAT/TileBeam/HBA1", - "STAT/DigitalBeam/HBA1", - "STAT/TemperatureManager/1" - ] - } - } - } - } - }, "EC": { "STAT": { "EC": { diff --git a/CDB/stations/simulators_ConfigDb.json b/CDB/stations/simulators_ConfigDb.json index e298e70aec6729b537cd7a2b6fcaa80f8b0dbe12..6ce4102b6ae394ebbd930f03138c1a4227dc1487 100644 --- a/CDB/stations/simulators_ConfigDb.json +++ b/CDB/stations/simulators_ConfigDb.json @@ -1,18 +1,5 @@ { "servers": { - "Boot": { - "STAT": { - "Boot": { - "STAT/Boot/1": { - "properties": { - "Initialise_Hardware": [ - "False" - ] - } - } - } - } - }, "APSCT": { "STAT": { "APSCT": { diff --git a/CDB/stations/testenv_cs001.json b/CDB/stations/testenv_cs001.json index 6bf8d12a9634b50792f34d68180fe7bd6e038647..33bfdb2e053659e535755c5b64b78185ebfef18f 100644 --- a/CDB/stations/testenv_cs001.json +++ b/CDB/stations/testenv_cs001.json @@ -1,18 +1,5 @@ { "servers": { - "Boot": { - "STAT": { - "Boot": { - "STAT/Boot/1": { - "properties": { - "Initialise_Hardware": [ - "False" - ] - } - } - } - } - }, "APSCT": { "STAT": { "APSCT": { diff --git a/README.md b/README.md index 904e96ee4334e230f97f00f77fe166015cb360f1..e42628c4dc01fd993ee3fcf65f7f22be56cb2515 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,7 @@ Next change the version in the following places: # Release Notes +* 0.21.2 Removed deprecated "Boot" device (use StationManager now) * 0.21.1 Implement multi project integration downstream pipeline * 0.21.0 Use radians instead of degrees when interpreting pointings * 0.20.5 Manage both polarisations in RCU_band_select_R(W), Antenna_Loss_R, and Frequency_Band_RW diff --git a/docker-compose/device-boot.yml b/docker-compose/device-boot.yml deleted file mode 100644 index 0ca0d485483cffe81b4ca12a9abb4fa21e3635f3..0000000000000000000000000000000000000000 --- a/docker-compose/device-boot.yml +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy) -# SPDX-License-Identifier: Apache-2.0 -# -# Docker compose file that launches a LOFAR2.0 station's -# ObservationControl device. It also runs the dynamically -# created Observation devices. -# -# Defines: -# - device-observation-control: LOFAR2.0 station ObvservationControl -# -# Requires: -# - lofar-device-base.yml -# -version: '2.1' - -services: - device-boot: - image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/lofar-device-base:${TAG} - hostname: device-boot - container_name: device-boot - logging: - driver: "json-file" - options: - max-size: "100m" - max-file: "10" - networks: - - control - ports: - - "5708:5708" # unique port for this DS - - "5808:5808" # ZeroMQ event port - - "5908:5908" # ZeroMQ heartbeat port - extra_hosts: - - "host.docker.internal:host-gateway" - volumes: - - ..:/opt/lofar/tango:rw - environment: - - TANGO_HOST=${TANGO_HOST} - - TANGO_ZMQ_EVENT_PORT=5808 - - TANGO_ZMQ_HEARTBEAT_PORT=5908 - healthcheck: - test: l2ss-health dserver/BOOT/STAT - interval: 1m - timeout: 30s - retries: 3 - start_period: 30s - working_dir: /opt/lofar/tango - entrypoint: - - bin/start-ds.sh - # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA - # can't know about our Docker port forwarding - - l2ss-boot Boot STAT -v -ORBendPoint giop:tcp:0:5708 -ORBendPointPublish giop:tcp:${HOSTNAME}:5708 - restart: on-failure - stop_signal: SIGINT # request a graceful shutdown of Tango - stop_grace_period: 2s diff --git a/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py b/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py index 36179e8ceabf679aa860da7575a8d6b8e86efd07..c3a4210e1752e29983a4b2a0ccc8fa1b37b8a730 100644 --- a/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py +++ b/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py @@ -70,7 +70,6 @@ tilebeam_h1 = OptionalDeviceProxy("STAT/TileBeam/HBA1") antennafield_h1 = OptionalDeviceProxy("STAT/AntennaField/HBA1") stationmanager = OptionalDeviceProxy("STAT/StationManager/1") -boot = OptionalDeviceProxy("STAT/Boot/1") ccd = OptionalDeviceProxy("STAT/CCD/1") ec = OptionalDeviceProxy("STAT/EC/1") pcon = OptionalDeviceProxy("STAT/PCON/1") @@ -83,7 +82,6 @@ configuration = OptionalDeviceProxy("STAT/Configuration/1") devices = ( [ stationmanager, - boot, ccd, ec, pcon, diff --git a/docker-compose/tango-prometheus-exporter/lofar2-fast-policy.json b/docker-compose/tango-prometheus-exporter/lofar2-fast-policy.json index ef26b8bd9d15492b7fd2add30d659f7408e42e4a..fe6010562b48d3ae6e78b70c46befe4a17c462d5 100644 --- a/docker-compose/tango-prometheus-exporter/lofar2-fast-policy.json +++ b/docker-compose/tango-prometheus-exporter/lofar2-fast-policy.json @@ -18,8 +18,6 @@ }, "stat/beamlet/*": { }, - "stat/boot/1": { - }, "stat/stationmanager/1": { }, "stat/digitalbeam/*": { diff --git a/docker-compose/tango-prometheus-exporter/lofar2-policy.json b/docker-compose/tango-prometheus-exporter/lofar2-policy.json index d6a867b7cf268556e73c0c0ac662fb73c6771df8..27a43c2757467e21dd052d454b120cb5bd5afca9 100644 --- a/docker-compose/tango-prometheus-exporter/lofar2-policy.json +++ b/docker-compose/tango-prometheus-exporter/lofar2-policy.json @@ -46,8 +46,6 @@ "FPGA_bf_weights_*" ] }, - "stat/boot/1": { - }, "stat/bst/*": { "exclude": [ "bst_R", diff --git a/docker-compose/tango-prometheus-exporter/lofar2-slow-policy.json b/docker-compose/tango-prometheus-exporter/lofar2-slow-policy.json index f05afaf8f253767f63bcd6c8154b64b0c51e1aaf..4e65340d110e507e33e84f9ed502cda994ed37a0 100644 --- a/docker-compose/tango-prometheus-exporter/lofar2-slow-policy.json +++ b/docker-compose/tango-prometheus-exporter/lofar2-slow-policy.json @@ -26,8 +26,6 @@ }, "stat/beamlet/*": { }, - "stat/boot/1": { - }, "stat/stationmanager/1": { }, "stat/digitalbeam/*": { diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh index afed289d534538656b245244f157379b5157e151..00745ce0da6e704f505f8adc235c7370daebab7c 100755 --- a/sbin/run_integration_test.sh +++ b/sbin/run_integration_test.sh @@ -98,7 +98,7 @@ echo '/usr/local/bin/wait-for-it.sh ${TANGO_HOST} --strict --timeout=300 -- true # Devices list is used to explitly word split when supplied to commands, must # disable shellcheck SC2086 for each case. -DEVICES=(device-station-manager device-boot device-aps device-apsct device-ccd device-ec device-apspu device-sdpfirmware device-sdp device-recvh device-recvl device-bst device-sst device-unb2 device-xst device-beamlet device-digitalbeam device-tilebeam device-psoc device-pcon device-antennafield device-temperature-manager device-observation-control device-configuration device-calibration) +DEVICES=(device-station-manager device-aps device-apsct device-ccd device-ec device-apspu device-sdpfirmware device-sdp device-recvh device-recvl device-bst device-sst device-unb2 device-xst device-beamlet device-digitalbeam device-tilebeam device-psoc device-pcon device-antennafield device-temperature-manager device-observation-control device-configuration device-calibration) SIMULATORS=(sdptr-sim recvh-sim recvl-sim unb2-sim apsct-sim apspu-sim ccd-sim ec-sim) diff --git a/tangostationcontrol/VERSION b/tangostationcontrol/VERSION index a67cebaf7ff61ccbf2741283f4341147d2fadae8..59dad104b0bad39d16c6edb862296e363e72ea8d 100644 --- a/tangostationcontrol/VERSION +++ b/tangostationcontrol/VERSION @@ -1 +1 @@ -0.21.1 +0.21.2 diff --git a/tangostationcontrol/docs/source/devices/boot.rst b/tangostationcontrol/docs/source/devices/boot.rst deleted file mode 100644 index c5d351fc89a6d1c35aa9123d81d9ef0c55003ca5..0000000000000000000000000000000000000000 --- a/tangostationcontrol/docs/source/devices/boot.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. _boot: - -Boot -==================== - -The ``boot == DeviceProxy("STAT/Boot/1")`` device is responsible for (re)starting and initialising the other devices. Devices which are not reachable, for example because their docker container is explicitly stopped, are skipped during initialisation. It is the only device that starts in the ``ON`` state, so it can be used immediately to initialise other devices. - -This device provides the following commands: - -:boot(): Start the other devices in the correct order, if they are not ON. Set their default values, and command them to initialise their hardware. This procedure runs asynchronously, causing this command to return immediately. Use the attributes defined below to track the boot progress. - - :returns: ``None`` - -:warm_boot(): Like ``boot()``, but do not initialise the hardware. Just start our software devices. - - :returns: ``None`` - -:reboot(): Like ``boot()``, but turn off all other devices first. - - :returns: ``None`` - -The initialisation process can subsequently be followed through monitoring the following attributes: - -:booting_R: Whether the initialisation procedure is still ongoing. - - :type: ``bool`` - -:progress_R: Percentage completeness of the initialisation procedure. Each succesfully configured device increments progress. - - :type: ``int`` - -:status_R: A description of what the device is currently trying to do. If an error occurs, this will hint towards the cause. - - :type: ``str`` - -:initialised_devices_R: Which devices were initialised succesfully. - - :type: ``str[]`` - -:uninitialised_devices_R: Which devices have not yet been initialised, or failed to initialiase. - - :type: ``str[]`` - -A useful pattern is thus to call ``boot()``, wait for ``booting_R == False``, and then check whether the initalisation was succesful, if ``uninitialised_devices_R == []``. If a device fails to initialise, check its status (``<device>.status()``), or consult the :doc:`../interfaces/logs`. diff --git a/tangostationcontrol/docs/source/devices/overview.rst b/tangostationcontrol/docs/source/devices/overview.rst index c1db108117f629507f59dc9f4ebcdf57c46e6396..cdfced254406b66474750e67b0da0a30d48c3ea2 100644 --- a/tangostationcontrol/docs/source/devices/overview.rst +++ b/tangostationcontrol/docs/source/devices/overview.rst @@ -99,5 +99,4 @@ Auxilliary devices that control hardware are: Finally, the stack holds the auxilliary devices that control the software devices. They connect to too many devices to draw: * `Docker` device controls the Docker containers of the software stack, -* `Boot` device controls the proces for booting up the devices and initialising the hardware, * `TemperatureManager` device acts on temperature alarms originating from the hardware. diff --git a/tangostationcontrol/docs/source/devices/using.rst b/tangostationcontrol/docs/source/devices/using.rst index 9a95ab24e182ac3f3d0c4032d0a0053ec59914b9..c23f3e60ab34ebef04de4db9f3e5324ee6b9f69f 100644 --- a/tangostationcontrol/docs/source/devices/using.rst +++ b/tangostationcontrol/docs/source/devices/using.rst @@ -115,8 +115,6 @@ Most devices provide the following commands, in order to configure the hardware :on(): Mark the device as operational. Moves from ``STANDBY`` to ``ON``. -See also :ref:`boot`, which provides functionality to initialise all the devices. - .. _attributes: Attributes diff --git a/tangostationcontrol/docs/source/index.rst b/tangostationcontrol/docs/source/index.rst index 8fe720c9ff899d84cd9f035b5dce0259cdb4ed3d..261a62f2668e848c06e44303f1aeca0f2b9811a3 100644 --- a/tangostationcontrol/docs/source/index.rst +++ b/tangostationcontrol/docs/source/index.rst @@ -27,7 +27,6 @@ Even without having access to any LOFAR2.0 hardware, you can install the full st devices/sdpfirmware devices/sdp devices/bst-sst-xst - devices/boot devices/station-manager devices/docker devices/psoc diff --git a/tangostationcontrol/docs/source/installation.rst b/tangostationcontrol/docs/source/installation.rst index c30706928eda3b3fa99f3064eb5da6b92f0fa8af..f7f11a4ca192adcf1d074857912ba165257bcca9 100644 --- a/tangostationcontrol/docs/source/installation.rst +++ b/tangostationcontrol/docs/source/installation.rst @@ -40,29 +40,13 @@ After bootstrapping, and after a reboot, the software and hardware of the statio The following commands start all the software devices to control the station hardware, and initialise the hardware with the configured default settings. Go to http://localhost:8888, start a new *Station Control* notebook, and initiate the software boot sequence:: # start and initialise the other devices - boot.boot() - - # wait for the devices to be initialised - import time - - while boot.booting_R: - print(f"Still initialising station. {boot.progress_R}% complete. State: {boot.status_R}") - time.sleep(1) - - # print conclusion - if boot.progress_R == 100: - print("Done initialising station.") - else: - print(f"Failed to initialise station: {boot.status_R}") - - # print what did and did not get initialised - print(f"Initialised devices: {boot.initialised_devices_R}") - print(f"Uninitialised devices: {boot.uninitialised_devices_R}") - -See :ref:`boot` for more information on the ``boot`` device. + # go through the full startup sequence + # OFF -> HIBERNATE -> STANDBY -> ON + stationmanager.station_hibernate() + stationmanager.station_standby() + stationmanager.station_on() Configuration --------------------------- These sections are optional, to configure specific functionality you may or may not want to use. - diff --git a/tangostationcontrol/integration_test/default/devices/test_device_boot.py b/tangostationcontrol/integration_test/default/devices/test_device_boot.py deleted file mode 100644 index dc91a137272b0729390c1d1d4cafb4b8db8513cc..0000000000000000000000000000000000000000 --- a/tangostationcontrol/integration_test/default/devices/test_device_boot.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy) -# SPDX-License-Identifier: Apache-2.0 - -import time - -from integration_test import base -from integration_test.device_proxy import TestDeviceProxy - -from tango import DevState - -from tangostationcontrol.common.constants import DEFAULT_POLLING_PERIOD - - -CS001_DEVICES = [ - "STAT/Configuration/1", - "STAT/PSOC/1", - "STAT/PCON/1", - "STAT/APSPU/L0", - "STAT/APSCT/L0", - "STAT/RECVL/L0", - "STAT/UNB2/L0", - "STAT/APSPU/L1", - "STAT/APSCT/L1", - "STAT/RECVL/L1", - "STAT/UNB2/L1", - "STAT/APSPU/H0", - "STAT/APSCT/H0", - "STAT/RECVH/H0", - "STAT/UNB2/H0", - "STAT/CCD/1", - "STAT/SDP/LBA", - "STAT/BST/LBA", - "STAT/SST/LBA", - "STAT/XST/LBA", - "STAT/Beamlet/LBA", - "STAT/AntennaField/LBA", - "STAT/DigitalBeam/LBA", - "STAT/SDP/HBA0", - "STAT/BST/HBA0", - "STAT/SST/HBA0", - "STAT/XST/HBA0", - "STAT/Beamlet/HBA0", - "STAT/AntennaField/HBA0", - "STAT/TileBeam/HBA0", - "STAT/DigitalBeam/HBA0", - "STAT/SDP/HBA1", - "STAT/BST/HBA1", - "STAT/SST/HBA1", - "STAT/XST/HBA1", - "STAT/Beamlet/HBA1", - "STAT/AntennaField/HBA1", - "STAT/TileBeam/HBA1", - "STAT/DigitalBeam/HBA1", - "STAT/TemperatureManager/1", -] - - -class TestDeviceBoot(base.IntegrationTestCase): - def setUp(self): - self.proxy = TestDeviceProxy("STAT/Boot/1") - self.proxy.put_property({"Device_Names": CS001_DEVICES}) - - def test_start_in_on(self): - """Test whether we start in the ON state""" - - self.assertEqual(DevState.ON, self.proxy.state()) - - def test_reboot(self): - """Test if we can reinitialise the station""" - - # This attribute needs to be polled for the - # TemperatureManager test to succesfully initialise - TestDeviceProxy("STAT/RECVH/H0").poll_attribute( - "HBAT_LED_on_RW", DEFAULT_POLLING_PERIOD - ) - - self.proxy.reboot() - - # wait for a few seconds for the station to initialise - timeout = 10 - while self.proxy.booting_R and timeout: - time.sleep(1) - - # check whether initialisation succeeded - self.assertEqual( - 100, - self.proxy.progress_R, - msg=f"Initialisation of station failed. Status: {self.proxy.status_R}", - ) diff --git a/tangostationcontrol/tangostationcontrol/devices/boot.py b/tangostationcontrol/tangostationcontrol/devices/boot.py deleted file mode 100644 index f08f23d702119ff15b2e314b55fc076dbeda0b55..0000000000000000000000000000000000000000 --- a/tangostationcontrol/tangostationcontrol/devices/boot.py +++ /dev/null @@ -1,424 +0,0 @@ -# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy) -# SPDX-License-Identifier: Apache-2.0 - -""" Boot Device Server for LOFAR2.0 - -Boots the rest of the station software. - -""" - -import logging - -# Additional import -import numpy -from tango import AttrWriteType, DevState - -# PyTango imports -from tango import DebugIt -from tango.server import command -from tango.server import device_property, attribute - -from tangostationcontrol.common.entrypoint import entry -from tangostationcontrol.common.lofar_logging import ( - device_logging_to_python, - log_exceptions, -) -from tangostationcontrol.common.proxy import create_device_proxy -from tangostationcontrol.common.states import OPERATIONAL_STATES -from tangostationcontrol.devices.device_decorators import only_in_states -from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDevice - -logger = logging.getLogger() - -from threading import Thread - -__all__ = ["Boot", "main"] - - -class InitialisationException(Exception): - pass - - -class DevicesInitialiser(object): - """ - Initialise devices on this station which are not already on (reboot=False), - or all of them (reboot=True). - - Devices which are unreachable are assumed to be brought down explicitly, - and are ignored. - - Initialisation happens in a separate thread. It is started by calling - 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=60.0 - ): - self.reboot = reboot - self.initialise_hardware = initialise_hardware - - self.device_names = device_names - self.proxy_timeout = proxy_timeout - - # setup initial state - self.thread = None - self.progress = 0 - self.devices = [] - self.device_initialised = {name: False for name in device_names} - self.set_status("Initialisation not started yet") - - def _get_device_proxies(self): - """Obtain the Device Proxies to all the devices we are to initialise.""" - - # Since Python3.7+, the insertion order equals the iteration order, - # which is what we depend on to process the devices in the same order as in device_names. - self.set_status("Obtaining DeviceProxies") - devices = {} - for name in self.device_names: - self.set_status(f"Obtaining a DeviceProxy to {name}") - devices[name] = create_device_proxy(name, int(self.proxy_timeout * 1000)) - - return devices - - def run(self): - self.set_status("Starting initialisation") - - try: - # get the device proxies if we didn't already - self.devices = self.devices or self._get_device_proxies() - - # initialise the devices - self.set_status("Initialisation started") - self.initialise_devices() - - # if we get here without throwing an exception, we're done - self.set_status("Initialisation completed") - except Exception as _e: - logger.exception("Failed to initialise station") - - # add the exception to the status - self.set_status( - f"ERROR: {self.status} [{_e.__class__.__name__}: {str(_e)}]" - ) - - # we keep the status stuck at the last thing it tried - - def is_running(self): - return self.thread and self.thread.is_alive() - - def start(self): - if self.is_running(): - # still busy, don't start - return - - if self.thread: - # done, but thread still exist. reap it first - self.stop() - - self.thread = Thread(target=self.run) - self.thread.start() - - def stop(self): - if not self.is_running(): - return - - # Just wait for the current initialisation to finish. It's a finite process. - self.thread.join() - - self.thread = None - - def set_status(self, status): - self.status = status - - logger.info(status) - - def initialise_devices(self): - """ - Initialise or re-initialise all devices on the station. - - If a device fails to initialise, the process is stopped. Calling - this function again will resume initialisation from the failed device. - - :return:None - """ - - # reset initialisation parameters - self.progress = 0 - - logger.debug(self.devices.keys()) - # restart devices in order - for num_restarted_devices, device in enumerate(self.devices.keys(), 1): - # allow resuming by skipping already initialised devices - if self.device_initialised[device]: - logger.debug("%s already initialised", device) - continue - - if self.is_available(device): - logger.debug("%s available", device) - if ( - self.reboot - or self.devices[device].state() not in OPERATIONAL_STATES - ): - self.stop_device(device) - self.boot_device(device) - - # mark device as initialised - self.device_initialised[device] = True - - self.progress = 100.0 * num_restarted_devices / len(self.devices) - - # make sure we always finish at 100% in case of success - self.progress = 100 - - def is_available(self, device_name: str): - """Return whether the device 'device_name' is actually available on this server.""" - - proxy = self.devices[device_name] - try: - proxy.state() - except Exception as _e: - return False - - return True - - def stop_device(self, device_name: str): - """Stop device 'device_name'.""" - - proxy = self.devices[device_name] - - if proxy.state() == DevState.OFF: - # already off - return - - self.set_status(f"[stopping {device_name}] Stopping device.") - - proxy.Off() - if proxy.state() != DevState.OFF: - raise InitialisationException( - f"Could not turn off device {device_name}. It reports status: {proxy.status()}" - ) - - self.set_status(f"[stopping {device_name}] Stopped device.") - - def boot_device(self, device_name: str): - """Run the startup sequence for device 'device_name'.""" - - proxy = self.devices[device_name] - - self.set_status(f"[restarting {device_name}] Booting device.") - proxy.boot() - if self.initialise_hardware: - proxy.power_hardware_on() - - if proxy.state() not in OPERATIONAL_STATES: - raise InitialisationException( - f"Could not boot device {device_name}. It reports status: {proxy.status()}" - ) - - self.set_status(f"[restarting {device_name}] Succesfully booted.") - - -@device_logging_to_python() -class Boot(LOFARDevice): - # maximum number of devices boot.py supports - MAX_BOOT_DEVICES = 128 - - # ----------------- - # Device Properties - # ----------------- - - DeviceProxy_Time_Out = device_property( - dtype="DevDouble", - mandatory=False, - default_value=60.0, - ) - - # Initialise the hardware when initialising a station. Can end badly when using simulators. - Initialise_Hardware = device_property( - dtype="DevBoolean", - mandatory=False, - default_value=True, - ) - - # Which devices to initialise, and in which order - Device_Names = device_property( - dtype="DevVarStringArray", - mandatory=False, - default_value=[ - # "STAT/Docker/1", # TODO(JDM): disabled as it doesn't work in gitlab CI/CD dind. - # Docker controls the device containers, so it goes before anything else - "STAT/Configuration/1", - # Configuration device loads and update station configuration - "STAT/PSOC/1", - # PSOC boot early to detect power delivery failure as fast as possible - "STAT/PCON/1", - # PCON boot early because it is responsible for power delivery. - "STAT/APSPU/1", - # APS Power Units control other hardware we want to initialise - "STAT/APSCT/1", - "STAT/CCD/1", - # RCUs are input for SDP, so initialise them first - "STAT/RECVH/1", - "STAT/RECVL/1", - "STAT/UNB2/1", # Uniboards host SDP, so initialise them first - "STAT/SDPFirmware/HBA", - "STAT/SDP/HBA", - # SDP controls the mask for SST/XST/BST/Beamlet, so initialise it first - "STAT/BST/HBA", - "STAT/SST/HBA", - "STAT/XST/HBA", - "STAT/Beamlet/HBA", - "STAT/AntennaField/HBA", # Accesses RECVH - # "STAT/AntennaField/LBA", # Accesses RECVL - "STAT/TileBeam/HBA", # Accesses AntennaField - # "STAT/TileBeam/LBA", # Accesses AntennaField - "STAT/DigitalBeam/HBA", # Accessed SDP and Beamlet - # "STAT/DigitalBeam/LBA", # Accessed SDP and Beamlet - "STAT/TemperatureManager/1", - "STAT/Calibration/1", - # Calibration device loads and update station calibration - ], - ) - - # ---------- - # Attributes - # ---------- - booting_R = attribute( - dtype=bool, - access=AttrWriteType.READ, - fget=lambda self: self.initialiser.is_running(), - doc="Whether booting is in progress.", - ) - progress_R = attribute( - dtype=numpy.int32, - access=AttrWriteType.READ, - fget=lambda self: numpy.int32(self.initialiser.progress), - doc="Percentage of devices that was initialised", - ) - status_R = attribute( - dtype=str, - access=AttrWriteType.READ, - fget=lambda self: self.initialiser.status, - doc="Description of current boot activity", - ) - initialised_devices_R = attribute( - dtype=(str,), - max_dim_x=MAX_BOOT_DEVICES, - access=AttrWriteType.READ, - fget=lambda self: [ - name - for name, initialised in self.initialiser.device_initialised.items() - if initialised - ], - doc="Which devices were initialised succesfully", - ) - uninitialised_devices_R = attribute( - dtype=(str,), - max_dim_x=MAX_BOOT_DEVICES, - access=AttrWriteType.READ, - fget=lambda self: [ - name - for name, initialised in self.initialiser.device_initialised.items() - if not initialised - ], - doc="Which devices have not been initialised or failed to initialise", - ) - - # -------- - # overloaded functions - # -------- - def init_device(self): - super().init_device() - - # always turn on automatically, so the user doesn't have to boot the boot device - self.Initialise() - self.On() - - # Override to avoid having this called during Initialise, which we call - # from init_device. The original clear_poll_cache requires a functioning - # self.proxy, which is not yet the case during init_device. The proxy - # needs a running server_loop, which is started after init_device. - def clear_poll_cache(self): - pass - - @log_exceptions() - def configure_for_off(self): - """user code here. is called when the state is set to OFF""" - try: - self.initialiser.stop() - except Exception as _e: - logger.warning( - "Exception while stopping OPC ua connection in configure_for_off function: %s. \ - Exception ignored", - _e, - ) - - @log_exceptions() - def configure_for_initialise(self): - # create an initialiser object so we can query it - # even before starting the (first) initialisation - self.initialiser = DevicesInitialiser( - self.Device_Names, - False, - self.Initialise_Hardware, - self.DeviceProxy_Time_Out, - ) - - def _boot(self, reboot=False, initialise_hardware=True): - """ - Initialise all devices on the station that are not yet on (reboot=False), - or shut them down first (reboot=True). - - If initialise_hardware is set, the hardware behind the devices is also - explicitly reinitialised. Turn this off to perform a warm boot. - - This command will take a while to execute, so should be called asynchronously. - - :return:None - """ - - if self.initialiser.is_running(): - # already initialising - return - - # join any previous attempt, if any - try: - self.initialiser.stop() - except RuntimeError: - pass - - # start new initialisation attempt - self.initialiser = DevicesInitialiser( - self.Device_Names, reboot, initialise_hardware, self.DeviceProxy_Time_Out - ) - self.initialiser.start() - - @command() - @DebugIt() - @only_in_states(OPERATIONAL_STATES) - @log_exceptions() - def boot(self): - self._boot(reboot=False, initialise_hardware=self.Initialise_Hardware) - - @command() - @DebugIt() - @only_in_states(OPERATIONAL_STATES) - @log_exceptions() - def reboot(self): - self._boot(reboot=True, initialise_hardware=self.Initialise_Hardware) - - @command() - @DebugIt() - @only_in_states([DevState.ON]) - @log_exceptions() - def warm_boot(self): - self._boot(reboot=False, initialise_hardware=False) - - -# ---------- -# Run server -# ---------- -def main(**kwargs): - """Main function of the Boot module.""" - return entry(Boot, **kwargs)