Skip to content
Snippets Groups Projects
Commit ac01b353 authored by Taya Snijder's avatar Taya Snijder
Browse files

re added PCC and SDP

parents 63cbd9b2 0893a6f7
Branches
Tags
1 merge request!52021 03 22 branched from master attribute wrapper
Showing
with 403 additions and 1 deletion
# -*- coding: utf-8 -*-
#
# This file is part of the SDP project
#
#
#
# Distributed under the terms of the APACHE license.
# See LICENSE.txt for more info.
"""SDP Device Server for LOFAR2.0
"""
from . import release
from .SDP import SDP, main
__version__ = release.version
__version_info__ = release.version_info
__author__ = release.author
# -*- coding: utf-8 -*-
#
# This file is part of the SDP project
#
#
#
# Distributed under the terms of the APACHE license.
# See LICENSE.txt for more info.
from SDP import main
main()
from threading import Thread
import socket
import time
__all__ = ["OPCUAConnection"]
class OPCUAConnection(Thread):
"""
Connects to OPC-UA in the foreground or background, and sends HELLO
messages to keep a check on the connection. On connection failure, reconnects once.
"""
def __init__(self, client, on_func, fault_func, streams, try_interval=2):
super().__init__(daemon=True)
self.client = client
self.on_func = on_func
self.fault_func = fault_func
self.try_interval = try_interval
self.streams = streams
self.stopping = False
self.connected = False
def _servername(self):
return self.client.server_url.geturl()
def connect(self):
try:
self.streams.debug_stream("Connecting to server %s", self._servername())
self.client.connect()
self.connected = True
self.streams.debug_stream("Connected to server. Initialising.")
return True
except socket.error as e:
self.streams.error_stream("Could not connect to server %s: %s", self._servername(), e)
return False
def disconnect(self):
self.connected = False # always force a reconnect, regardless of a successful disconnect
try:
self.client.disconnect()
except Exception as e:
self.streams.error_stream("Disconnect from OPC-UA server %s failed: %s", self._servername(), e)
def run(self):
while not self.stopping:
# keep trying to connect
if not self.connected:
if self.connect():
self.on_func()
else:
# we retry only once, to catch exotic network issues. if the infra or hardware is down,
# our device cannot help, and must be reinitialised after the infra or hardware is fixed.
self.fault_func()
return
# keep checking if the connection is still alive
try:
while not self.stopping:
self.client.send_hello()
time.sleep(self.try_interval)
except Exception as e:
self.streams.error_stream("Lost connection to server %s: %s", self._servername(), e)
# technically, we may not have dropped the connection, but encounter a different error. so explicitly disconnect.
self.disconnect()
# signal that we're disconnected
self.fault_func()
def stop(self):
"""
Stop connecting & disconnect. Can take a few seconds for the timeouts to hit.
"""
if not self.ident:
# have not yet been started, so nothing to do
return
self.stopping = True
self.join()
self.disconnect()
# -*- coding: utf-8 -*-
#
# This file is part of the SDP project
#
#
#
# Distributed under the terms of the APACHE license.
# See LICENSE.txt for more info.
"""Release information for Python Package"""
name = """tangods-sdp"""
version = "1.0.0"
version_info = version.split(".")
description = """"""
author = "Thomas Juerges"
author_email = "jurges at astron.nl"
license = """APACHE"""
url = """https://git.astron.nl/lofar2.0/tango.git"""
copyright = """"""
from tango import DevState, Except
from functools import wraps
import traceback
__all__ = ["only_in_states", "only_when_on", "fault_on_error"]
def only_in_states(allowed_states):
"""
Wrapper to call and return the wrapped function if the device is
in one of the given states. Otherwise a PyTango exception is thrown.
"""
def wrapper(func):
@wraps(func)
def state_check_wrapper(self, *args, **kwargs):
if self.get_state() in allowed_states:
return func(self, *args, **kwargs)
self.warn_stream("Illegal command: Function %s can only be called in states %s. Current state: %s" % (func.__name__, allowed_states, self.get_state()))
Except.throw_exception("IllegalCommand", "Function can only be called in states %s. Current state: %s" % (allowed_states, self.get_state()), func.__name__)
return state_check_wrapper
return wrapper
def only_when_on(func):
"""
Wrapper to call and return the wrapped function if the device is
in the ON state. Otherwise None is returned and nothing
will be called.
"""
@wraps(func)
def when_on_wrapper(self, *args, **kwargs):
if self.get_state() == DevState.ON:
return func(self, *args, **kwargs)
return None
return when_on_wrapper
def fault_on_error(func):
"""
Wrapper to catch exceptions. Sets the device in a FAULT state if any occurs.
"""
@wraps(func)
def error_wrapper(self, *args, **kwargs):
try:
return func(self, *args, **kwargs)
except Exception as e:
self.error_stream("Function failed. Trace: %s", traceback.format_exc())
self.Fault()
return None
return error_wrapper
opcua >= 0.98.9
numpy
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This file is part of the SDP project
#
#
#
# Distributed under the terms of the APACHE license.
# See LICENSE.txt for more info.
import os
import sys
from setuptools import setup
setup_dir = os.path.dirname(os.path.abspath(__file__))
# make sure we use latest info from local code
sys.path.insert(0, setup_dir)
readme_filename = os.path.join(setup_dir, 'README.rst')
with open(readme_filename) as file:
long_description = file.read()
release_filename = os.path.join(setup_dir, 'SDP', 'release.py')
exec(open(release_filename).read())
pack = ['SDP']
setup(name=name,
version=version,
description='',
packages=pack,
include_package_data=True,
test_suite="test",
entry_points={'console_scripts':['SDP = SDP:main']},
author='Thomas Juerges',
author_email='jurges at astron.nl',
license='APACHE',
long_description=long_description,
url='https://git.astron.nl/lofar2.0/tango.git',
platforms="Unix Like"
)
...@@ -92,6 +92,8 @@ pull: ## pull the images from the Docker hub ...@@ -92,6 +92,8 @@ pull: ## pull the images from the Docker hub
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) pull $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) pull
build: ## rebuild images 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 $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build
up: minimal ## start the base TANGO system and prepare all services up: minimal ## start the base TANGO system and prepare all services
...@@ -122,6 +124,9 @@ attach: ## attach a service to an existing Tango network ...@@ -122,6 +124,9 @@ attach: ## attach a service to an existing Tango network
status: ## show the container status status: ## show the container status
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) ps $(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 clean: down ## clear all TANGO database entries
docker volume rm $(BASEDIR)_tangodb docker volume rm $(BASEDIR)_tangodb
......
#
# 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
#
# 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
...@@ -37,3 +37,4 @@ services: ...@@ -37,3 +37,4 @@ services:
- --strict - --strict
- -- - --
- /venv/bin/itango3 - /venv/bin/itango3
restart: on-failure
...@@ -18,6 +18,7 @@ services: ...@@ -18,6 +18,7 @@ services:
volumes: volumes:
- ${TANGO_SKA_CONTAINER_MOUNT} - ${TANGO_SKA_CONTAINER_MOUNT}
- ${TANGO_LOFAR_CONTAINER_MOUNT} - ${TANGO_LOFAR_CONTAINER_MOUNT}
- ${TANGO_LOFAR_LOCAL_DIR}/jupyter-notebooks:/jupyter-notebooks:rw
- ${HOME}:/hosthome - ${HOME}:/hosthome
environment: environment:
- TANGO_HOST=${TANGO_HOST} - TANGO_HOST=${TANGO_HOST}
...@@ -25,7 +26,7 @@ services: ...@@ -25,7 +26,7 @@ services:
- DISPLAY=${DISPLAY} - DISPLAY=${DISPLAY}
ports: ports:
- "8888:8888" - "8888:8888"
working_dir: ${TANGO_LOFAR_CONTAINER_DIR}/jupyter-notebooks working_dir: /jupyter-notebooks
entrypoint: entrypoint:
- /usr/local/bin/wait-for-it.sh - /usr/local/bin/wait-for-it.sh
- ${TANGO_HOST} - ${TANGO_HOST}
...@@ -33,3 +34,4 @@ services: ...@@ -33,3 +34,4 @@ services:
- --strict - --strict
- -- - --
- /usr/bin/tini -- jupyter notebook --port=8888 --no-browser --ip=0.0.0.0 --allow-root --NotebookApp.token= --NotebookApp.password= - /usr/bin/tini -- jupyter notebook --port=8888 --no-browser --ip=0.0.0.0 --allow-root --NotebookApp.token= --NotebookApp.password=
restart: on-failure
#
# 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}
FROM nexus.engageska-portugal.pt/ska-docker/tango-itango:latest
COPY lofar-requirements.txt /lofar-requirements.txt
RUN pip3 install -r /lofar-requirements.txt
opcua >= 0.98.9
astropy
...@@ -16,3 +16,4 @@ services: ...@@ -16,3 +16,4 @@ services:
- ${HOME}:/hosthome - ${HOME}:/hosthome
ports: ports:
- "4842:4842" - "4842:4842"
restart: on-failure
%% Cell type:code id:social-massachusetts tags:
``` python
def force_start(device):
if device.state() == DevState.FAULT:
device.Off()
if device.state() == DevState.OFF:
device.initialise()
if device.state() == DevState.INIT:
device.Standby()
if device.state() == DevState.STANDBY:
device.On()
return device.state()
```
%% Cell type:code id:defined-apache tags:
``` python
for d in devices:
print("Device %s is now in state %s" % (d, force_start(d)))
```
%% Output
Device PCC(lts/pcc/1) is now in state FAULT
Device SDP(lts/sdp/1) is now in state ON
%% Cell type:code id:superior-wheel tags:
``` python
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment