From 11793e6c443dd9a452fe372e0715a5786c376b5d Mon Sep 17 00:00:00 2001
From: stedif <stefano.difrischia@inaf.it>
Date: Mon, 24 Oct 2022 11:59:39 +0200
Subject: [PATCH] L2SS-1030: create skeleton for configuration device

---
 .gitlab-ci.yml                                | 26 ++++++---
 CDB/LOFAR_ConfigDb.json                       |  7 +++
 docker-compose/device-configuration.yml       | 53 +++++++++++++++++++
 .../startup/01-devices.py                     |  3 +-
 .../startup/01-devices.py                     |  3 +-
 .../lofar2-policy.json                        |  2 +
 sbin/run_integration_test.sh                  |  2 +-
 sbin/tag_and_push_docker_image.sh             |  1 +
 .../docs/source/devices/configuration.rst     |  7 +++
 tangostationcontrol/docs/source/index.rst     |  1 +
 tangostationcontrol/setup.cfg                 |  1 +
 .../tangostationcontrol/devices/README.md     |  2 +-
 .../tangostationcontrol/devices/boot.py       |  1 +
 .../devices/configuration_device.py           |  8 +++
 .../devices/docker_device.py                  |  2 +
 .../devices/test_device_configuration.py      | 16 ++++++
 16 files changed, 124 insertions(+), 11 deletions(-)
 create mode 100644 docker-compose/device-configuration.yml
 create mode 100644 tangostationcontrol/docs/source/devices/configuration.rst
 create mode 100644 tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_configuration.py

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 82cbc3f55..f86c6aae2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -106,26 +106,27 @@ docker_build_image_all:
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh recv-sim latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh sdptr-sim latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh unb2-sim latest
+    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-antennafield latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-apsct latest
-    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-ccd latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-apspu latest
-    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-tilebeam latest
+    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-boot latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-beamlet latest
+    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-ccd latest
+    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-configuration latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-digitalbeam latest
-    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-antennafield latest
-    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-boot latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-docker latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-observation latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-observation-control latest
-    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-psoc latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-pcon latest
+    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-psoc latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-recv latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-sdp latest
+    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-temperature-manager latest
+    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-tilebeam latest
+    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-unb2 latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-bst latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-sst latest
-    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-unb2 latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-xst latest
-    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-temperature-manager latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh archiver-timescale latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh hdbpp latest
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh hdbppts-cm latest
@@ -310,6 +311,17 @@ docker_build_image_device_ccd:
   script:
 #    Do not remove 'bash' or statement will be ignored by primitive docker shell
     - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-ccd $tag
+docker_build_image_device_configuration:
+  extends: .base_docker_images_except
+  only:
+    refs:
+      - merge_requests
+    changes:
+      - docker-compose/device-configuration.yml
+      - docker-compose/lofar-device-base/*
+  script:
+#    Do not remove 'bash' or statement will be ignored by primitive docker shell
+    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh device-configuration $tag
 docker_build_image_device_apspu:
   extends: .base_docker_images_except
   only:
diff --git a/CDB/LOFAR_ConfigDb.json b/CDB/LOFAR_ConfigDb.json
index b2d2f517e..0370c7c90 100644
--- a/CDB/LOFAR_ConfigDb.json
+++ b/CDB/LOFAR_ConfigDb.json
@@ -17,6 +17,13 @@
                 }
             }
         },
+        "Configuration": {
+            "STAT": {
+                "Configuration": {
+                    "STAT/Configuration/1": {}
+                }
+            }
+        },
         "Observation": {
             "STAT": {
                 "Observation": {
diff --git a/docker-compose/device-configuration.yml b/docker-compose/device-configuration.yml
new file mode 100644
index 000000000..3a7f1a624
--- /dev/null
+++ b/docker-compose/device-configuration.yml
@@ -0,0 +1,53 @@
+#
+# Docker compose file that launches an interactive iTango session.
+#
+# Connect to the interactive session with 'docker attach itango'.
+# Disconnect with the Docker deattach sequence: <CTRL>+<P> <CTRL>+<Q>
+#
+# Defines:
+#   - itango: iTango interactive session
+#
+# Requires:
+#   - lofar-device-base.yml
+#
+version: '3'
+
+services:
+  device-configuration:
+    image: device-configuration
+    # build explicitly, as docker-compose does not understand a local image
+    # being shared among services.
+    build:
+        context: .
+        dockerfile: lofar-device-base/Dockerfile
+        args:
+            SOURCE_IMAGE: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-itango:${TANGO_ITANGO_VERSION}
+    container_name: ${CONTAINER_NAME_PREFIX}device-configuration
+    logging:
+      driver: "json-file"
+      options:
+        max-size: "100m"
+        max-file: "10"
+    networks:
+      - control
+    ports:
+      - "5722:5722" # unique port for this DS
+      - "5822:5822" # ZeroMQ event port
+      - "5922:5922" # ZeroMQ heartbeat port
+    extra_hosts:
+      - "host.docker.internal:host-gateway"
+    volumes:
+      - ..:/opt/lofar/tango:rw
+    environment:
+    - TANGO_HOST=${TANGO_HOST}
+    - TANGO_ZMQ_EVENT_PORT=5822
+    - TANGO_ZMQ_HEARTBEAT_PORT=5922
+    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-configuration-device Configuration STAT -v -ORBendPoint giop:tcp:device-configuration:5722 -ORBendPointPublish giop:tcp:${HOSTNAME}:5722
+    restart: on-failure
+    stop_signal: SIGINT # request a graceful shutdown of Tango
+    stop_grace_period: 2s
diff --git a/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py b/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
index 350ecb1e8..4b33c8132 100644
--- a/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
+++ b/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
@@ -16,6 +16,7 @@ digitalbeam = DeviceProxy("STAT/DigitalBeam/1")
 antennafield = DeviceProxy("STAT/AntennaField/1")
 docker = DeviceProxy("STAT/Docker/1")
 temperaturemanager = DeviceProxy("STAT/TemperatureManager/1")
+configuration = DeviceProxy("STAT/Configuration/1")
 
 # Put them in a list in case one wants to iterate
-devices = [apsct, ccd, apspu, recv, sdp, bst, sst, xst, unb2, boot, tilebeam, beamlet, digitalbeam, antennafield, temperaturemanager, docker]
+devices = [apsct, ccd, apspu, recv, sdp, bst, sst, xst, unb2, boot, tilebeam, beamlet, digitalbeam, antennafield, temperaturemanager, docker, configuration]
diff --git a/docker-compose/jupyterlab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py b/docker-compose/jupyterlab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
index 350ecb1e8..4b33c8132 100644
--- a/docker-compose/jupyterlab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
+++ b/docker-compose/jupyterlab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
@@ -16,6 +16,7 @@ digitalbeam = DeviceProxy("STAT/DigitalBeam/1")
 antennafield = DeviceProxy("STAT/AntennaField/1")
 docker = DeviceProxy("STAT/Docker/1")
 temperaturemanager = DeviceProxy("STAT/TemperatureManager/1")
+configuration = DeviceProxy("STAT/Configuration/1")
 
 # Put them in a list in case one wants to iterate
-devices = [apsct, ccd, apspu, recv, sdp, bst, sst, xst, unb2, boot, tilebeam, beamlet, digitalbeam, antennafield, temperaturemanager, docker]
+devices = [apsct, ccd, apspu, recv, sdp, bst, sst, xst, unb2, boot, tilebeam, beamlet, digitalbeam, antennafield, temperaturemanager, docker, configuration]
diff --git a/docker-compose/tango-prometheus-exporter/lofar2-policy.json b/docker-compose/tango-prometheus-exporter/lofar2-policy.json
index 606f06499..bfb587489 100644
--- a/docker-compose/tango-prometheus-exporter/lofar2-policy.json
+++ b/docker-compose/tango-prometheus-exporter/lofar2-policy.json
@@ -17,6 +17,8 @@
         },
         "stat/ccd/1": {
         },
+        "stat/configuration/1": {
+        },
         "stat/apspu/1": {
         },
         "stat/beamlet/1": {
diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh
index 563b2c7fb..ffd18c715 100755
--- a/sbin/run_integration_test.sh
+++ b/sbin/run_integration_test.sh
@@ -76,7 +76,7 @@ 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 -
 
-DEVICES="device-boot device-apsct device-ccd device-apspu device-sdp device-recv 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 device-observation-control"
+DEVICES="device-boot device-apsct device-ccd device-apspu device-sdp device-recv 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 device-observation-control device-configuration"
 
 SIMULATORS="sdptr-sim recv-sim unb2-sim apsct-sim apspu-sim ccd-sim"
 
diff --git a/sbin/tag_and_push_docker_image.sh b/sbin/tag_and_push_docker_image.sh
index d3b5ea894..c785e4c8f 100755
--- a/sbin/tag_and_push_docker_image.sh
+++ b/sbin/tag_and_push_docker_image.sh
@@ -72,6 +72,7 @@ LOCAL_IMAGES=(
   "device-antennafield device-antennafield y"
   "device-apsct device-apsct y" "device-apspu device-apspu y"
   "device-ccd device-ccd y"
+  "device-configuration device-configuration y"
   "device-boot device-boot y" "device-docker device-docker y"
   "device-observation device-observation y"
   "device-observation-control device-observation-control y"
diff --git a/tangostationcontrol/docs/source/devices/configuration.rst b/tangostationcontrol/docs/source/devices/configuration.rst
new file mode 100644
index 000000000..65f8c0280
--- /dev/null
+++ b/tangostationcontrol/docs/source/devices/configuration.rst
@@ -0,0 +1,7 @@
+.. _configuration:
+
+Configuration
+--------------------
+
+The ``Configuration == DeviceProxy("STAT/Configuration/1")`` Configuration Device controls the loading, updating, exposing 
+and dumping of the whole Station Configuration
diff --git a/tangostationcontrol/docs/source/index.rst b/tangostationcontrol/docs/source/index.rst
index 4c56206df..250804f4e 100644
--- a/tangostationcontrol/docs/source/index.rst
+++ b/tangostationcontrol/docs/source/index.rst
@@ -30,6 +30,7 @@ Even without having access to any LOFAR2.0 hardware, you can install the full st
    devices/docker
    devices/psoc
    devices/ccd
+   devices/configuration
    devices/temperature-manager
    devices/configure
    configure_station
diff --git a/tangostationcontrol/setup.cfg b/tangostationcontrol/setup.cfg
index f42357ef7..36318820c 100644
--- a/tangostationcontrol/setup.cfg
+++ b/tangostationcontrol/setup.cfg
@@ -56,6 +56,7 @@ console_scripts =
     l2ss-unb2 = tangostationcontrol.devices.unb2:main
     l2ss-xst = tangostationcontrol.devices.sdp.xst:main
     l2ss-temperature-manager = tangostationcontrol.devices.temperature_manager:main
+    l2ss-configuration-device = tangostationcontrol.devices.configuration_device:main
 
 # The following entry points should eventually be removed / replaced
     l2ss-hardware-device-template = tangostationcontrol.examples.HW_device_template:main
diff --git a/tangostationcontrol/tangostationcontrol/devices/README.md b/tangostationcontrol/tangostationcontrol/devices/README.md
index 64390631b..0a1081baa 100644
--- a/tangostationcontrol/tangostationcontrol/devices/README.md
+++ b/tangostationcontrol/tangostationcontrol/devices/README.md
@@ -9,7 +9,7 @@ If a new device is added, it will (likely) need to be referenced in several plac
 - Adjust `CDB/LOFAR_ConfigDb.json` to create the device in the Tango device database,
 - Adjust `docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py` and `docker-compose/jupyterlab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py` to make an alias for it available in Jupyter and Jupyter-Lab,
 - Adjust `tangostationcontrol/tangostationcontrol/devices/boot.py` to add the device to the station initialisation sequence,
-- Add to `docker-compose/` to create a YaML file to start the device in a docker container. NOTE: it needs a unique 57xx port assigned (current _unused_ port value: 5722), a unique 58xx port for ZMQ events, and a unique 59xx port for ZMQ heartbeat
+- Add to `docker-compose/` to create a YaML file to start the device in a docker container. NOTE: it needs a unique 57xx port assigned (current _unused_ port value: 5723), a unique 58xx port for ZMQ events, and a unique 59xx port for ZMQ heartbeat
 - Adjust `tangostationcontrol/setup.cfg` to add an entry point for the device in the package installation,
 - Add to `tangostationcontrol/tangostationcontrol/integration_test/default/devices/` to add an integration test,
 - Adjust `sbin/run_integration_test.sh` to have the device started when running the integration tests,
diff --git a/tangostationcontrol/tangostationcontrol/devices/boot.py b/tangostationcontrol/tangostationcontrol/devices/boot.py
index 42ce74d74..e0899c743 100644
--- a/tangostationcontrol/tangostationcontrol/devices/boot.py
+++ b/tangostationcontrol/tangostationcontrol/devices/boot.py
@@ -251,6 +251,7 @@ class Boot(lofar_device):
                        "STAT/TileBeam/1",     # Accesses AntennaField
                        "STAT/DigitalBeam/1",  # Accessed SDP and Beamlet
                        "STAT/TemperatureManager/1",
+                       "STAT/Configuration/1",
                       ],
     )
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/configuration_device.py b/tangostationcontrol/tangostationcontrol/devices/configuration_device.py
index 9394c0705..bc8222cd3 100644
--- a/tangostationcontrol/tangostationcontrol/devices/configuration_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/configuration_device.py
@@ -40,6 +40,14 @@ class Configuration(lofar_device):
     def read_tangodb_properties_RW(self):
         return self._dump_configdb()
     
+    def write_tangodb_properties_RW(self, tangodb_properties):
+        """ Takes a JSON string which represents the station configuration 
+        and loads the whole configuration from scratch. 
+        
+        N.B. it does not update, it loads a full new configuration. 
+        """
+        self.proxy.tangodb_properties_RW = tangodb_properties
+    
     def _dump_configdb(self):
         """ Returns the TangoDB station configuration as a JSON string """
         return 'Configuration'
diff --git a/tangostationcontrol/tangostationcontrol/devices/docker_device.py b/tangostationcontrol/tangostationcontrol/devices/docker_device.py
index 71e13119d..a746ad1cd 100644
--- a/tangostationcontrol/tangostationcontrol/devices/docker_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/docker_device.py
@@ -48,6 +48,8 @@ class Docker(lofar_device):
     device_boot_R = attribute_wrapper(comms_annotation={"container": "device-boot"}, datatype=bool)
     device_boot_RW = attribute_wrapper(comms_annotation={"container": "device-boot"}, datatype=bool, access=AttrWriteType.READ_WRITE)
     device_docker_R = attribute_wrapper(comms_annotation={"container": "device-docker"}, datatype=bool)
+    device_configuration_R = attribute_wrapper(comms_annotation={"container": "device-configuration"}, datatype=bool)
+    device_configuration_RW = attribute_wrapper(comms_annotation={"container": "device-configuration"}, datatype=bool, access=AttrWriteType.READ_WRITE)
     # device_docker_RW is not available, as we cannot start our own container`
     device_temperature_manager_R = attribute_wrapper(comms_annotation={"container": "device-temperature-manager"}, datatype=bool)
     device_temperature_manager_RW = attribute_wrapper(comms_annotation={"container": "device-temperature-manager"}, datatype=bool, access=AttrWriteType.READ_WRITE)
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_configuration.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_configuration.py
new file mode 100644
index 000000000..4a34cad03
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_configuration.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+from .base import AbstractTestBases
+
+
+class TestDeviceConfiguration(AbstractTestBases.TestDeviceBase):
+
+    def setUp(self):
+        super().setUp("STAT/Configuration/1")
-- 
GitLab