diff --git a/PCC/PCC/PCC.py b/PCC/PCC/PCC.py index 1b8fca093bbd64af1c1ddd5a657857ae26da3136..ce4de28c8c0f5f0e45dfe006ecc46e86f5d4a49b 100644 --- a/PCC/PCC/PCC.py +++ b/PCC/PCC/PCC.py @@ -87,6 +87,10 @@ class PCC(Device): # Attributes # ---------- + RCU_state_R = attribute( + dtype = (numpy.str), + ) + RCU_mask_RW = attribute( dtype=(numpy.bool_,), max_dim_x=32, @@ -204,6 +208,11 @@ class PCC(Device): access=AttrWriteType.READ_WRITE, ) + uC_ID_R = attribute( + dtype=(numpy.int64,), + max_dim_x=32, + ) + RCU_monitor_rate_RW = attribute( dtype=numpy.float_, access=AttrWriteType.READ_WRITE, @@ -233,6 +242,8 @@ class PCC(Device): self.debug_stream("Mapping OPC-UA MP/CP to attributes...") + self.attribute_mapping["RCU_state_R"] = self.get_pcc_node("RCU_state_R") + self.attribute_mapping["RCU_mask_RW"] = self.get_pcc_node("RCU_mask_RW") self.attribute_mapping["Ant_mask_RW"] = self.get_pcc_node("Ant_mask_RW") @@ -277,6 +288,8 @@ class PCC(Device): self.attribute_mapping["HBA_element_pwr_RW"] = self.get_pcc_node("HBA_element_pwr_RW") + self.attribute_mapping["uC_ID_R"] = self.get_pcc_node("uC_ID _R") + self.attribute_mapping["RCU_monitor_rate_RW"] = self.get_pcc_node("RCU_monitor_rate_RW") self.function_mapping["RCU_off"] = self.get_pcc_node("RCU_off") @@ -314,6 +327,9 @@ class PCC(Device): # Set default values in the RW/R attributes and add them to # the mapping. + self._RCU_state_R = "" + self.attribute_mapping["RCU_state_R"] = {} + self._RCU_mask_RW = numpy.full(32, False) self.attribute_mapping["RCU_mask_RW"] = {} @@ -380,6 +396,9 @@ class PCC(Device): self._HBA_element_pwr_RW = numpy.full((96, 32), 0) self.attribute_mapping["HBA_element_pwr_RW"] = {} + self._uC_ID_R = numpy.full(32, 0) + self.attribute_mapping["uC_ID_R"] = {} + self._RCU_monitor_rate_RW = 30.0 self.attribute_mapping["RCU_monitor_rate_RW"] = {} @@ -450,6 +469,13 @@ class PCC(Device): # ------------------ # Attributes methods # ------------------ + @only_when_on + @fault_on_error + def read_RCU_state_R(self): + """Return the RCU_state_R attribute.""" + self._RCU_state_R = self.attribute_mapping["RCU_state_R"].get_value() + return self._RCU_state_R + @only_when_on @fault_on_error def read_RCU_mask_R(self): @@ -675,6 +701,13 @@ class PCC(Device): self.attribute_mapping["HBA_element_pwr_RW"].set_value(value.flatten().tolist()) self._HBA_element_pwr_RW = value + @only_when_on + @fault_on_error + def read_uC_ID_R(self): + """Return the uC_ID_R attribute.""" + self._uC_ID_R = numpy.array(self.attribute_mapping["uC_ID_R"].get_value()) + return self._uC_ID_R + @only_when_on @fault_on_error def read_RCU_monitor_rate_RW(self): diff --git a/docker-compose/Makefile b/docker-compose/Makefile index 6c2e767c8dfcac11cd1fb1d54f035e7b73b1f48a..65de8951a33821b2f06c3b530b11b2f3c1fbc355 100644 --- a/docker-compose/Makefile +++ b/docker-compose/Makefile @@ -92,6 +92,8 @@ pull: ## pull the images from the Docker hub $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) pull build: ## rebuild images + # docker-compose does not support build dependencies, so manage those here + $(DOCKER_COMPOSE_ARGS) docker-compose -f lofar-device-base.yml build $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build up: minimal ## start the base TANGO system and prepare all services @@ -122,6 +124,9 @@ attach: ## attach a service to an existing Tango network status: ## show the container status $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) ps +images: ## show the container images + $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) images + clean: down ## clear all TANGO database entries docker volume rm $(BASEDIR)_tangodb diff --git a/docker-compose/device-pcc.yml b/docker-compose/device-pcc.yml new file mode 100644 index 0000000000000000000000000000000000000000..96eb6901583216b69035b1d2e906a2c1744e6644 --- /dev/null +++ b/docker-compose/device-pcc.yml @@ -0,0 +1,31 @@ +# +# 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: '2' + +services: + device-pcc: + image: lofar-device-base + container_name: ${CONTAINER_NAME_PREFIX}device-pcc + network_mode: ${NETWORK_MODE} + volumes: + - ${TANGO_LOFAR_CONTAINER_MOUNT} + environment: + - TANGO_HOST=${TANGO_HOST} + entrypoint: + - /usr/local/bin/wait-for-it.sh + - ${TANGO_HOST} + - --timeout=30 + - --strict + - -- + - python3 -u ${TANGO_LOFAR_CONTAINER_DIR}/PCC/PCC LTS -v + restart: on-failure diff --git a/docker-compose/device-sdp.yml b/docker-compose/device-sdp.yml new file mode 100644 index 0000000000000000000000000000000000000000..8fbc3cbc536360658446d2f21c57231d29b8d6ca --- /dev/null +++ b/docker-compose/device-sdp.yml @@ -0,0 +1,31 @@ +# +# 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: '2' + +services: + device-sdp: + image: lofar-device-base + container_name: ${CONTAINER_NAME_PREFIX}device-sdp + network_mode: ${NETWORK_MODE} + volumes: + - ${TANGO_LOFAR_CONTAINER_MOUNT} + environment: + - TANGO_HOST=${TANGO_HOST} + entrypoint: + - /usr/local/bin/wait-for-it.sh + - ${TANGO_HOST} + - --timeout=30 + - --strict + - -- + - python3 -u ${TANGO_LOFAR_CONTAINER_DIR}/SDP/SDP LTS -v + restart: on-failure diff --git a/docker-compose/itango.yml b/docker-compose/itango.yml index ef825b5c20526f2db03e8d03f26bbdf5c145ee5c..d131d405371bc6ff7e59892587b2bd2aba9d1fd6 100644 --- a/docker-compose/itango.yml +++ b/docker-compose/itango.yml @@ -37,3 +37,4 @@ services: - --strict - -- - /venv/bin/itango3 + restart: on-failure diff --git a/docker-compose/jupyter.yml b/docker-compose/jupyter.yml index 7f0011446935a0c49cef406f5addb870d3fad515..f1970916f7e9facc25908c08c63f0938be0c4eb7 100644 --- a/docker-compose/jupyter.yml +++ b/docker-compose/jupyter.yml @@ -18,6 +18,7 @@ services: volumes: - ${TANGO_SKA_CONTAINER_MOUNT} - ${TANGO_LOFAR_CONTAINER_MOUNT} + - ${TANGO_LOFAR_LOCAL_DIR}/jupyter-notebooks:/jupyter-notebooks:rw - ${HOME}:/hosthome environment: - TANGO_HOST=${TANGO_HOST} @@ -25,7 +26,7 @@ services: - DISPLAY=${DISPLAY} ports: - "8888:8888" - working_dir: ${TANGO_LOFAR_CONTAINER_DIR}/jupyter-notebooks + working_dir: /jupyter-notebooks entrypoint: - /usr/local/bin/wait-for-it.sh - ${TANGO_HOST} @@ -33,3 +34,4 @@ services: - --strict - -- - /usr/bin/tini -- jupyter notebook --port=8888 --no-browser --ip=0.0.0.0 --allow-root --NotebookApp.token= --NotebookApp.password= + restart: on-failure diff --git a/docker-compose/lofar-device-base.yml b/docker-compose/lofar-device-base.yml new file mode 100644 index 0000000000000000000000000000000000000000..3d40f0f63554222907e6094bc61f1342cc8fabfa --- /dev/null +++ b/docker-compose/lofar-device-base.yml @@ -0,0 +1,21 @@ +# +# Docker compose file that forms the basis for LOFAR tango devices +# +# This is an abstract image that does not need to be spinned up, but +# might be out of consistency with other images. +# +# Defines: +# - device-base: Base configuration for devices. +# +# Requires: +# - tango.yml +# +version: '2' + +services: + lofar-device-base: + image: lofar-device-base + build: + context: lofar-device-base + container_name: ${CONTAINER_NAME_PREFIX}lofar-device-base + network_mode: ${NETWORK_MODE} diff --git a/docker-compose/lofar-device-base/Dockerfile b/docker-compose/lofar-device-base/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..cd6b59511f7e0a3e515ab282775edeadec6b9aaf --- /dev/null +++ b/docker-compose/lofar-device-base/Dockerfile @@ -0,0 +1,4 @@ +FROM nexus.engageska-portugal.pt/ska-docker/tango-itango:latest + +COPY lofar-requirements.txt /lofar-requirements.txt +RUN pip3 install -r /lofar-requirements.txt diff --git a/docker-compose/lofar-device-base/lofar-requirements.txt b/docker-compose/lofar-device-base/lofar-requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..90d21efe0e5ac0601204d4f05ce7efcd16dca2de --- /dev/null +++ b/docker-compose/lofar-device-base/lofar-requirements.txt @@ -0,0 +1,2 @@ +opcua >= 0.98.9 +astropy diff --git a/docker-compose/pypcc-sim.yml b/docker-compose/pypcc-sim.yml index bb6d519f2e98a3beb8bc34711d44f16f8aad29ce..d1c47c0465affd6b7bcb2de27dfa340c48e681ce 100644 --- a/docker-compose/pypcc-sim.yml +++ b/docker-compose/pypcc-sim.yml @@ -16,3 +16,4 @@ services: - ${HOME}:/hosthome ports: - "4842:4842" + restart: on-failure diff --git a/jupyter-notebooks/Start All Devices.ipynb b/jupyter-notebooks/Start All Devices.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..beb52a381c89a4cda30b08374d36c337def29eae --- /dev/null +++ b/jupyter-notebooks/Start All Devices.ipynb @@ -0,0 +1,73 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "id": "social-massachusetts", + "metadata": {}, + "outputs": [], + "source": [ + "def force_start(device):\n", + " if device.state() == DevState.FAULT:\n", + " device.Off()\n", + " if device.state() == DevState.OFF:\n", + " device.initialise()\n", + " if device.state() == DevState.INIT:\n", + " device.Standby()\n", + " if device.state() == DevState.STANDBY:\n", + " device.On()\n", + " \n", + " return device.state()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "defined-apache", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Device PCC(lts/pcc/1) is now in state FAULT\n", + "Device SDP(lts/sdp/1) is now in state ON\n" + ] + } + ], + "source": [ + "for d in devices:\n", + " print(\"Device %s is now in state %s\" % (d, force_start(d)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "superior-wheel", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "StationControl", + "language": "python", + "name": "stationcontrol" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}