diff --git a/.gitignore b/.gitignore index af7bbfbee9775dc73c6cb0081ccc537a3213be74..7489d2a10dba8069c8cc10309b20ea61e5397aef 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,7 @@ **/.project **/.pydevproject **/.settings/org.eclipse.core.resources.prefs -**/dist +tangostationcontrol/dist +tangostationcontrol/build **/.ipynb_checkpoints +**/pending_log_messages.db diff --git a/devices/.stestr.conf b/devices/.stestr.conf deleted file mode 100644 index 07147c8697683f270e9388da8b914c20cb8e4c45..0000000000000000000000000000000000000000 --- a/devices/.stestr.conf +++ /dev/null @@ -1,3 +0,0 @@ -[DEFAULT] -test_path=${TESTS_DIR:-./test} -top_dir=./ diff --git a/devices/LICENSE.txt b/devices/LICENSE.txt deleted file mode 100644 index 8a0eaeb196094a651006f51fd99c0c05cb16ccd6..0000000000000000000000000000000000000000 --- a/devices/LICENSE.txt +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright 2021 ASTRON Netherlands Institute for Radio Astronomy - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/devices/__init__.py b/devices/__init__.py deleted file mode 100644 index 329b61fa54fd8d6afeee2937b3f6cd2c12efb711..0000000000000000000000000000000000000000 --- a/devices/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from util.lofar_git import get_version - -__version__ = get_version() diff --git a/devices/setup.cfg b/devices/setup.cfg deleted file mode 100644 index 26fb9e4fbb77178f32cfd9589904f8315bb17597..0000000000000000000000000000000000000000 --- a/devices/setup.cfg +++ /dev/null @@ -1,45 +0,0 @@ -[metadata] -name = TangoStationControl -summary = LOFAR 2.0 Station Control -description_file = - README.md -description_content_type = text/x-rst; charset=UTF-8 -author = ASTRON -home_page = https://astron.nl -project_urls = - Bug Tracker = https://support.astron.nl/jira/projects/L2SS/issues/ - Source Code = https://git.astron.nl/lofar2.0/tango -license = Apache-2 -classifier = - Environment :: Console - License :: Apache Software License - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - -[files] -package_dir=./ - -[entry_points] -console_scripts = - l2ss-docker-device = devices.docker_device:main - l2ss-observation = devices.observation:main - l2ss-observation-control = devices.observation_control:main - l2ss-receiver = devices.recv:main - l2ss-sdp = devices.sdp.sdp:main - l2ss-sst = devices.sdp.sst:main - l2ss-unb2 = devices.unb2:main - l2ss-xst = devices.sdp.xst:main - -# The following entry points should eventually be removed / replaced - l2ss-cold-start = toolkit.lts_cold_start:main - l2ss-hardware-device-template = examples.HW_device_template:main - l2ss-ini-device = examples.load_from_disc.ini_device:main - l2ss-parse-statistics-packet = devices.sdp.statistics_packet:main - l2ss-random-data = test.devices.random_data:main - l2ss-snmp = examples.snmp.snmp:main - l2ss-version = common.lofar_git:main diff --git a/docker-compose/integration-test.yml b/docker-compose/integration-test.yml index e0d1c6baf58948cdbee5a71ff2f859ab429dcd4b..23b114b08916f5850bc6a89ec26dcc1c45935cfd 100644 --- a/docker-compose/integration-test.yml +++ b/docker-compose/integration-test.yml @@ -19,7 +19,7 @@ services: - ..:/opt/lofar/tango:rw environment: - TANGO_HOST=${TANGO_HOST} - working_dir: /opt/lofar/tango/devices + working_dir: /opt/lofar/tango/tangostationcontrol entrypoint: - /usr/local/bin/wait-for-it.sh - ${TANGO_HOST} diff --git a/tangostationcontrol/.stestr.conf b/tangostationcontrol/.stestr.conf new file mode 100644 index 0000000000000000000000000000000000000000..59b161cddad8a253059cf201b8872bc946f78c64 --- /dev/null +++ b/tangostationcontrol/.stestr.conf @@ -0,0 +1,3 @@ +[DEFAULT] +test_path=${TESTS_DIR:-./tangostationcontrol/test} +top_dir=./ diff --git a/devices/README.md b/tangostationcontrol/README.md similarity index 100% rename from devices/README.md rename to tangostationcontrol/README.md diff --git a/devices/requirements.txt b/tangostationcontrol/requirements.txt similarity index 100% rename from devices/requirements.txt rename to tangostationcontrol/requirements.txt diff --git a/tangostationcontrol/setup.cfg b/tangostationcontrol/setup.cfg new file mode 100644 index 0000000000000000000000000000000000000000..fa8fefb94c391064d9d461d0b0b51a53251f7847 --- /dev/null +++ b/tangostationcontrol/setup.cfg @@ -0,0 +1,45 @@ +[metadata] +name = TangoStationControl +summary = LOFAR 2.0 Station Control +description_file = + README.md +description_content_type = text/x-rst; charset=UTF-8 +author = ASTRON +home_page = https://astron.nl +project_urls = + Bug Tracker = https://support.astron.nl/jira/projects/L2SS/issues/ + Source Code = https://git.astron.nl/lofar2.0/tango +license = Apache-2 +classifier = + Environment :: Console + License :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + +[files] +packages=tangostationcontrol + +[entry_points] +console_scripts = + l2ss-docker-device = tangostationcontrol.devices.docker_device:main + l2ss-observation = tangostationcontrol.devices.observation:main + l2ss-observation-control = tangostationcontrol.devices.observation_control:main + l2ss-receiver = tangostationcontrol.devices.recv:main + l2ss-sdp = tangostationcontrol.devices.sdp.sdp:main + l2ss-sst = tangostationcontrol.devices.sdp.sst:main + l2ss-unb2 = tangostationcontrol.devices.unb2:main + l2ss-xst = tangostationcontrol.devices.sdp.xst:main + +# The following entry points should eventually be removed / replaced + l2ss-cold-start = tangostationcontrol.toolkit.lts_cold_start:main + l2ss-hardware-device-template = tangostationcontrol.examples.HW_device_template:main + l2ss-ini-device = tangostationcontrol.examples.load_from_disk.ini_device:main + l2ss-parse-statistics-packet = tangostationcontrol.devices.sdp.statistics_packet:main + l2ss-random-data = tangostationcontrol.test.devices.random_data:main + l2ss-snmp = tangostationcontrol.examples.snmp.snmp:main + l2ss-version = tangostationcontrol.common.lofar_git:main diff --git a/devices/setup.py b/tangostationcontrol/setup.py similarity index 100% rename from devices/setup.py rename to tangostationcontrol/setup.py diff --git a/tangostationcontrol/tangostationcontrol/__init__.py b/tangostationcontrol/tangostationcontrol/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b3cd94bd233d6a03b0a7b22fc878b1ec2d466ad5 --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/__init__.py @@ -0,0 +1,5 @@ +import pbr.version +# from util.lofar_git import get_version + +# __version__ = get_version() +__version__ = pbr.version.VersionInfo('TangoStationControl').version_string() diff --git a/devices/clients/README.md b/tangostationcontrol/tangostationcontrol/clients/README.md similarity index 100% rename from devices/clients/README.md rename to tangostationcontrol/tangostationcontrol/clients/README.md diff --git a/devices/devices/__init__.py b/tangostationcontrol/tangostationcontrol/clients/__init__.py similarity index 100% rename from devices/devices/__init__.py rename to tangostationcontrol/tangostationcontrol/clients/__init__.py diff --git a/devices/clients/attribute_wrapper.py b/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py similarity index 98% rename from devices/clients/attribute_wrapper.py rename to tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py index e55a662142cb89f62775fb7ac2189c063593df37..007b9ff86455d41a752168ad06558a3b220b8d79 100644 --- a/devices/clients/attribute_wrapper.py +++ b/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py @@ -2,7 +2,7 @@ from tango.server import attribute from tango import AttrWriteType import numpy -from devices.device_decorators import only_when_on, fault_on_error +from tangostationcontrol.devices.device_decorators import only_when_on, fault_on_error import logging logger = logging.getLogger() diff --git a/devices/clients/comms_client.py b/tangostationcontrol/tangostationcontrol/clients/comms_client.py similarity index 100% rename from devices/clients/comms_client.py rename to tangostationcontrol/tangostationcontrol/clients/comms_client.py diff --git a/devices/clients/docker_client.py b/tangostationcontrol/tangostationcontrol/clients/docker_client.py similarity index 100% rename from devices/clients/docker_client.py rename to tangostationcontrol/tangostationcontrol/clients/docker_client.py diff --git a/devices/clients/opcua_client.py b/tangostationcontrol/tangostationcontrol/clients/opcua_client.py similarity index 99% rename from devices/clients/opcua_client.py rename to tangostationcontrol/tangostationcontrol/clients/opcua_client.py index 2bde56237f8f61dcdc3abc4bdb4d03f7741746fb..f06a9ca85d2f02fafd4ada9e576393ae18f783f9 100644 --- a/devices/clients/opcua_client.py +++ b/tangostationcontrol/tangostationcontrol/clients/opcua_client.py @@ -1,11 +1,10 @@ -from threading import Thread import socket import numpy import asyncua import asyncio from asyncua import Client -from clients.comms_client import AsyncCommClient +from tangostationcontrol.clients.comms_client import AsyncCommClient import logging logger = logging.getLogger() diff --git a/devices/clients/statistics_client.py b/tangostationcontrol/tangostationcontrol/clients/statistics_client.py similarity index 98% rename from devices/clients/statistics_client.py rename to tangostationcontrol/tangostationcontrol/clients/statistics_client.py index 3fd470fbf0319e45242abbc3a79362584628f844..2790197bdb90f2483d872f0dfd5978fa088d4980 100644 --- a/devices/clients/statistics_client.py +++ b/tangostationcontrol/tangostationcontrol/clients/statistics_client.py @@ -6,7 +6,7 @@ from .comms_client import AsyncCommClient from .tcp_replicator import TCPReplicator from .udp_receiver import UDPReceiver -from devices.sdp.statistics_collector import StatisticsConsumer +from tangostationcontrol.devices.sdp.statistics_collector import StatisticsConsumer logger = logging.getLogger() diff --git a/devices/clients/statistics_client_thread.py b/tangostationcontrol/tangostationcontrol/clients/statistics_client_thread.py similarity index 100% rename from devices/clients/statistics_client_thread.py rename to tangostationcontrol/tangostationcontrol/clients/statistics_client_thread.py diff --git a/devices/clients/tcp_replicator.py b/tangostationcontrol/tangostationcontrol/clients/tcp_replicator.py similarity index 99% rename from devices/clients/tcp_replicator.py rename to tangostationcontrol/tangostationcontrol/clients/tcp_replicator.py index 5ac6e492d977cf14452d4f97bd213c0d12af7cbb..8b820cc765daa193fe142f4a3f49ef7a3f643c76 100644 --- a/devices/clients/tcp_replicator.py +++ b/tangostationcontrol/tangostationcontrol/clients/tcp_replicator.py @@ -6,7 +6,7 @@ from threading import Thread import asyncio import logging -from clients.statistics_client_thread import StatisticsClientThread +from tangostationcontrol.clients.statistics_client_thread import StatisticsClientThread logger = logging.getLogger() diff --git a/devices/clients/udp_receiver.py b/tangostationcontrol/tangostationcontrol/clients/udp_receiver.py similarity index 98% rename from devices/clients/udp_receiver.py rename to tangostationcontrol/tangostationcontrol/clients/udp_receiver.py index 8a9d1429945cdd5c41c47bf45edc5034c1cafa0c..2bda038a1fb0646af2d2e14082e10ca3d7866816 100644 --- a/devices/clients/udp_receiver.py +++ b/tangostationcontrol/tangostationcontrol/clients/udp_receiver.py @@ -7,7 +7,7 @@ import socket import time from typing import List # not needed for python3.9+, where we can use the type "list[Queue]" directly -from clients.statistics_client_thread import StatisticsClientThread +from tangostationcontrol.clients.statistics_client_thread import StatisticsClientThread logger = logging.getLogger() diff --git a/devices/devices/sdp/__init__.py b/tangostationcontrol/tangostationcontrol/common/__init__.py similarity index 100% rename from devices/devices/sdp/__init__.py rename to tangostationcontrol/tangostationcontrol/common/__init__.py diff --git a/devices/common/baselines.py b/tangostationcontrol/tangostationcontrol/common/baselines.py similarity index 100% rename from devices/common/baselines.py rename to tangostationcontrol/tangostationcontrol/common/baselines.py diff --git a/devices/common/lofar_git.py b/tangostationcontrol/tangostationcontrol/common/lofar_git.py similarity index 100% rename from devices/common/lofar_git.py rename to tangostationcontrol/tangostationcontrol/common/lofar_git.py diff --git a/devices/common/lofar_logging.py b/tangostationcontrol/tangostationcontrol/common/lofar_logging.py similarity index 98% rename from devices/common/lofar_logging.py rename to tangostationcontrol/tangostationcontrol/common/lofar_logging.py index 07f7f64e95819d2278ca0feb29ee2a28900b3012..18e07423d3f86f5de80513c24b0e626e14242c82 100644 --- a/devices/common/lofar_logging.py +++ b/tangostationcontrol/tangostationcontrol/common/lofar_logging.py @@ -1,7 +1,6 @@ import logging from functools import wraps from tango.server import Device -import sys import traceback import socket import time @@ -100,7 +99,7 @@ class LogAnnotator(logging.Formatter): # we just annotate, we don't filter return True -def configure_logger(logger: logging.Logger=None, log_extra=None, debug=False): +def configure_logger(logger: logging.Logger=None, log_extra=None): """ Configure the given logger (or root if None) to: - send logs to the ELK stack @@ -123,26 +122,6 @@ def configure_logger(logger: logging.Logger=None, log_extra=None, debug=False): # don't spam errors for git, as we use it in our log handler, which would result in an infinite loop logging.getLogger("git").setLevel(logging.ERROR) - # for now, also log to stderr - # Set up logging in a way that it can be understood by a human reader, be - # easily grep'ed, be parsed with a couple of shell commands and - # easily fed into an Kibana/Elastic search system. - handler = logging.StreamHandler() - - # Always also log the hostname because it makes the origin of the log clear. - hostname = socket.gethostname() - - formatter = logging.Formatter(fmt = '%(asctime)s.%(msecs)d %(levelname)s - HOST="{}" DEVICE="%(tango_device)s" PID="%(process)d" TNAME="%(threadName)s" FILE="%(pathname)s" LINE="%(lineno)d" FUNC="%(funcName)s" MSG="%(message)s"'.format(hostname), datefmt = '%Y-%m-%dT%H:%M:%S') - handler.setFormatter(formatter) - handler.addFilter(LogSuppressErrorSpam()) - handler.addFilter(LogAnnotator()) - - logger.addHandler(handler) - - # If configuring for debug; exit early - if debug: - return logger - # Log to ELK stack try: from logstash_async.handler import AsynchronousLogstashHandler, LogstashFormatter @@ -172,6 +151,23 @@ def configure_logger(logger: logging.Logger=None, log_extra=None, debug=False): except Exception: logger.exception("Cannot forward logs to Tango.") + + # for now, also log to stderr + # Set up logging in a way that it can be understood by a human reader, be + # easily grep'ed, be parsed with a couple of shell commands and + # easily fed into an Kibana/Elastic search system. + handler = logging.StreamHandler() + + # Always also log the hostname because it makes the origin of the log clear. + hostname = socket.gethostname() + + formatter = logging.Formatter(fmt = '%(asctime)s.%(msecs)d %(levelname)s - HOST="{}" DEVICE="%(tango_device)s" PID="%(process)d" TNAME="%(threadName)s" FILE="%(pathname)s" LINE="%(lineno)d" FUNC="%(funcName)s" MSG="%(message)s"'.format(hostname), datefmt = '%Y-%m-%dT%H:%M:%S') + handler.setFormatter(formatter) + handler.addFilter(LogSuppressErrorSpam()) + handler.addFilter(LogAnnotator()) + + logger.addHandler(handler) + return logger def device_logging_to_python(): diff --git a/devices/examples/__init__.py b/tangostationcontrol/tangostationcontrol/devices/__init__.py similarity index 100% rename from devices/examples/__init__.py rename to tangostationcontrol/tangostationcontrol/devices/__init__.py diff --git a/devices/devices/abstract_device.py b/tangostationcontrol/tangostationcontrol/devices/abstract_device.py similarity index 100% rename from devices/devices/abstract_device.py rename to tangostationcontrol/tangostationcontrol/devices/abstract_device.py diff --git a/devices/devices/device_decorators.py b/tangostationcontrol/tangostationcontrol/devices/device_decorators.py similarity index 100% rename from devices/devices/device_decorators.py rename to tangostationcontrol/tangostationcontrol/devices/device_decorators.py diff --git a/devices/devices/docker_device.py b/tangostationcontrol/tangostationcontrol/devices/docker_device.py similarity index 90% rename from devices/devices/docker_device.py rename to tangostationcontrol/tangostationcontrol/devices/docker_device.py index 5e786db6fbeda01b6a2fcf9b619e0df3e793d3cb..1f1fd245ecbf250d4729d5bb76941f9f4893b45b 100644 --- a/devices/devices/docker_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/docker_device.py @@ -12,7 +12,6 @@ """ # PyTango imports -from tango import DebugIt from tango.server import run, command from tango.server import device_property, attribute from tango import AttrWriteType @@ -22,10 +21,12 @@ import asyncio from device_decorators import * -from clients.docker_client import DockerClient -from clients.attribute_wrapper import attribute_wrapper -from devices.hardware_device import hardware_device -from common.lofar_logging import device_logging_to_python, log_exceptions +# Additional import +from tangostationcontrol.clients.docker_client import DockerClient +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.devices.hardware_device import hardware_device +from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions +from tangostationcontrol.common.lofar_git import get_version __all__ = ["Docker", "main"] @@ -44,6 +45,7 @@ class Docker(hardware_device): # ---------- # Attributes # ---------- + version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version()) archiver_maria_db_R = attribute_wrapper(comms_annotation={"container": "archiver-maria-db"}, datatype=numpy.bool_) archiver_maria_db_RW = attribute_wrapper(comms_annotation={"container": "archiver-maria-db"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) databaseds_R = attribute_wrapper(comms_annotation={"container": "databaseds"}, datatype=numpy.bool_) @@ -93,7 +95,7 @@ class Docker(hardware_device): try: self.docker_client.sync_stop() except Exception as e: - self.warn_stream("Exception while stopping docker client in configure_for_off function: {}. Exception ignored".format(e)) + self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e)) @log_exceptions() def configure_for_initialise(self): @@ -124,7 +126,7 @@ class Docker(hardware_device): def main(args=None, **kwargs): """Main function of the Docker module.""" - from common.lofar_logging import configure_logger + from tangostationcontrol.common.lofar_logging import configure_logger configure_logger() return run((Docker,), args=args, **kwargs) diff --git a/devices/devices/hardware_device.py b/tangostationcontrol/tangostationcontrol/devices/hardware_device.py similarity index 92% rename from devices/devices/hardware_device.py rename to tangostationcontrol/tangostationcontrol/devices/hardware_device.py index c812f432bb6249e2bc84d08ea0b004ac68e0cee8..7669cc15111619d3bfc05360406172a5bb4c796e 100644 --- a/devices/devices/hardware_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/hardware_device.py @@ -15,17 +15,19 @@ from abc import abstractmethod # PyTango imports from tango.server import Device, command, DeviceMeta, attribute -from tango import DevState, DebugIt, Attribute, DeviceProxy, AttrWriteType -# Additional import +from tango import DevState, DebugIt, Attribute, DeviceProxy -from clients.attribute_wrapper import attribute_wrapper -from common.lofar_logging import log_exceptions -from common.lofar_git import get_version -from devices.abstract_device import AbstractDeviceMetas -from devices.device_decorators import only_in_states, fault_on_error import time import math +# Additional import +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.common.lofar_logging import log_exceptions +from tangostationcontrol.common.lofar_git import get_version +from tangostationcontrol.devices.abstract_device import AbstractDeviceMetas +from tangostationcontrol.devices.device_decorators import only_in_states, fault_on_error + + __all__ = ["hardware_device"] import logging @@ -75,7 +77,6 @@ class hardware_device(Device, metaclass=AbstractDeviceMetas): self.value_dict = {i: i.initial_value() for i in self.attr_list()} - @log_exceptions() def init_device(self): """ Instantiates the device in the OFF state. """ @@ -84,19 +85,6 @@ class hardware_device(Device, metaclass=AbstractDeviceMetas): self.set_state(DevState.OFF) - @log_exceptions() - def delete_device(self): - """Hook to delete resources allocated in init_device. - - This method allows for any memory or other resources allocated in the - init_device method to be released. This method is called by the device - destructor and by the device Init command (a Tango built-in). - """ - logger.info("Shutting down...") - - self.Off() - logger.info("Shut down. Good bye.") - # -------- # Commands # -------- @@ -203,6 +191,18 @@ class hardware_device(Device, metaclass=AbstractDeviceMetas): """Method always executed before any TANGO command is executed.""" pass + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command (a Tango built-in). + """ + self.debug_stream("Shutting down...") + + self.Off() + self.debug_stream("Shut down. Good bye.") + @command() @only_in_states([DevState.STANDBY, DevState.ON]) @DebugIt() @@ -212,11 +212,6 @@ class hardware_device(Device, metaclass=AbstractDeviceMetas): A hardware point XXX is set to the value of the object member named XXX_default, if it exists. XXX_default can be f.e. a constant, or a device_property. - - The points are set in the following order: - 1) The python class property 'first_default_settings' is read, as an array of strings denoting property names. Each property - is set in that order. - 2) Any remaining default properties are set. """ # we cannot write directly to our attribute, as that would not diff --git a/devices/devices/observation.py b/tangostationcontrol/tangostationcontrol/devices/observation.py similarity index 95% rename from devices/devices/observation.py rename to tangostationcontrol/tangostationcontrol/devices/observation.py index 2297c5304abf9861ac820d186ebd5faeb9089d75..0dbec128dac155fb5243a8ead8f0de0be12dbda4 100644 --- a/devices/devices/observation.py +++ b/tangostationcontrol/tangostationcontrol/devices/observation.py @@ -11,9 +11,9 @@ from tango.server import Device, run, command, attribute import numpy from time import time -from devices.device_decorators import * -from common.lofar_logging import device_logging_to_python, log_exceptions -from common.lofar_git import get_version +from tangostationcontrol.devices.device_decorators import * +from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions +from tangostationcontrol.common.lofar_git import get_version from json import loads diff --git a/devices/devices/observation_control.py b/tangostationcontrol/tangostationcontrol/devices/observation_control.py similarity index 98% rename from devices/devices/observation_control.py rename to tangostationcontrol/tangostationcontrol/devices/observation_control.py index 0cba0af6aa6990c0f8a4ec837a92a115eb6f28d9..20bd9f546647833287c239bb5015661cb5850cda 100644 --- a/devices/devices/observation_control.py +++ b/tangostationcontrol/tangostationcontrol/devices/observation_control.py @@ -14,11 +14,10 @@ import numpy import time from json import loads -from devices.device_decorators import * -from common.lofar_logging import device_logging_to_python, log_exceptions -from common.lofar_git import get_version - -from observation import Observation +from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions +from tangostationcontrol.common.lofar_git import get_version +from tangostationcontrol.devices.device_decorators import * +from tangostationcontrol.devices.observation import Observation __all__ = ["ObservationControl", "main"] diff --git a/devices/devices/recv.py b/tangostationcontrol/tangostationcontrol/devices/recv.py similarity index 75% rename from devices/devices/recv.py rename to tangostationcontrol/tangostationcontrol/devices/recv.py index f63d1c4eda811ea2eaabbb46098e8a33cf4b3548..aa0d098cfdcaae3e62d3d5df3ef5ee88d844e50c 100644 --- a/devices/devices/recv.py +++ b/tangostationcontrol/tangostationcontrol/devices/recv.py @@ -17,43 +17,59 @@ from tango.server import run, command from tango.server import device_property, attribute from tango import AttrWriteType import numpy -# Additional import - -from device_decorators import * -from clients.attribute_wrapper import attribute_wrapper -from devices.opcua_device import opcua_device -from common.lofar_logging import device_logging_to_python, log_exceptions +# Additional import +from tangostationcontrol.clients.opcua_client import OPCUAConnection +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions +from tangostationcontrol.common.lofar_git import get_version +from tangostationcontrol.devices.device_decorators import * +from tangostationcontrol.devices.hardware_device import hardware_device __all__ = ["RECV", "main"] @device_logging_to_python() -class RECV(opcua_device): +class RECV(hardware_device): + """ + + **Properties:** + + - Device Property + OPC_Server_Name + - Type:'DevString' + OPC_Server_Port + - Type:'DevULong' + OPC_Time_Out + - Type:'DevDouble' + """ + # ----------------- # Device Properties # ----------------- - Ant_mask_RW_default = device_property( - dtype='DevVarBooleanArray', - mandatory=False, - default_value=[[True] * 3] * 32 + OPC_Server_Name = device_property( + dtype='DevString', + mandatory=True ) - RCU_mask_RW_default = device_property( - dtype='DevVarBooleanArray', - mandatory=False, - default_value=[True] * 32 + OPC_Server_Port = device_property( + dtype='DevULong', + mandatory=True ) - first_default_settings = [ - # set the masks first, as those filter any subsequent settings - 'Ant_mask_RW', - 'RCU_mask_RW' - ] + OPC_Time_Out = device_property( + dtype='DevDouble', + mandatory=True + ) + OPC_namespace = device_property( + dtype='DevString', + mandatory=False + ) # ---------- # Attributes # ---------- + version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version()) Ant_mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:Ant_mask_RW"], datatype=numpy.bool_, dims=(3, 32), access=AttrWriteType.READ_WRITE) Ant_status_R = attribute(dtype=str, max_dim_x=3, max_dim_y=32) CLK_Enable_PWR_R = attribute_wrapper(comms_annotation=["2:PCC", "2:CLK_Enable_PWR_R"], datatype=numpy.bool_) @@ -89,9 +105,55 @@ class RECV(opcua_device): RCU_translator_busy_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_translator_busy_R"], datatype=numpy.bool_) RCU_version_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_version_R"], datatype=numpy.str, dims=(32,)) + @log_exceptions() + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command (a Tango built-in). + """ + self.debug_stream("Shutting down...") + + self.Off() + self.debug_stream("Shut down. Good bye.") + # -------- # overloaded functions # -------- + @log_exceptions() + def configure_for_off(self): + """ user code here. is called when the state is set to OFF """ + # Stop keep-alive + try: + self.opcua_connection.stop() + except Exception as e: + self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e)) + + @log_exceptions() + def configure_for_initialise(self): + """ user code here. is called when the state is set to INIT """ + + # Init the dict that contains function to OPC-UA function mappings. + self.function_mapping = {} + self.function_mapping["RCU_on"] = {} + self.function_mapping["RCU_off"] = {} + self.function_mapping["CLK_on"] = {} + self.function_mapping["CLK_off"] = {} + + # set up the OPC ua client + self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Fault, self) + + # map an access helper class + for i in self.attr_list(): + try: + i.set_comm_client(self.OPCua_client) + except Exception as e: + # use the pass function instead of setting read/write fails + i.set_pass_func() + self.warn_stream("error while setting the RECV attribute {} read/write function. {}".format(i, e)) + + self.OPCua_client.start() # -------- # Commands @@ -261,7 +323,7 @@ class RECV(opcua_device): def main(args=None, **kwargs): """Main function of the RECV module.""" - from common.lofar_logging import configure_logger + from tangostationcontrol.common.lofar_logging import configure_logger configure_logger() return run((RECV,), args=args, **kwargs) diff --git a/devices/integration_test/__init__.py b/tangostationcontrol/tangostationcontrol/devices/sdp/__init__.py similarity index 100% rename from devices/integration_test/__init__.py rename to tangostationcontrol/tangostationcontrol/devices/sdp/__init__.py diff --git a/devices/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py similarity index 82% rename from devices/devices/sdp/sdp.py rename to tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py index 1365aba7782502d9418297101368b6e9de975e79..dd755853d3cd151fa0db4e6fc36c2c200b980784 100644 --- a/devices/devices/sdp/sdp.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py @@ -15,27 +15,51 @@ from tango.server import run from tango.server import device_property, attribute from tango import AttrWriteType -# Additional import -from clients.attribute_wrapper import attribute_wrapper -from devices.opcua_device import opcua_device +# Additional import +from tangostationcontrol.clients.opcua_client import OPCUAConnection +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.devices.hardware_device import hardware_device -from common.lofar_logging import device_logging_to_python, log_exceptions +from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions +from tangostationcontrol.common.lofar_git import get_version import numpy __all__ = ["SDP", "main"] @device_logging_to_python() -class SDP(opcua_device): +class SDP(hardware_device): + """ + + **Properties:** + + - Device Property + OPC_Server_Name + - Type:'DevString' + OPC_Server_Port + - Type:'DevULong' + OPC_Time_Out + - Type:'DevDouble' + """ + # ----------------- # Device Properties # ----------------- - TR_fpga_mask_RW_default = device_property( - dtype='DevVarBooleanArray', - mandatory=False, - default_value=[True] * 16 + OPC_Server_Name = device_property( + dtype='DevString', + mandatory=True + ) + + OPC_Server_Port = device_property( + dtype='DevULong', + mandatory=True + ) + + OPC_Time_Out = device_property( + dtype='DevDouble', + mandatory=True ) FPGA_processing_enable_RW_default = device_property( @@ -49,27 +73,6 @@ class SDP(opcua_device): mandatory=False, default_value=[[False] * 12] * 16 ) - - # If we enable the waveform generator, we want some sane defaults. - - FPGA_wg_amplitude_RW = device_property( - dtype='DevVarDoubleArray', - mandatory=False, - default_value=[[0.1] * 12] * 16 - ) - - FPGA_wg_frequency_RW = device_property( - dtype='DevVarDoubleArray', - mandatory=False, - # Emit a signal on subband 102 - default_value=[[102 * 200e6/1024] * 12] * 16 - ) - - FPGA_wg_phase_RW = device_property( - dtype='DevVarDoubleArray', - mandatory=False, - default_value=[[0.0] * 12] * 16 - ) FPGA_sdp_info_station_id_RW_default = device_property( dtype='DevVarULongArray', @@ -82,15 +85,12 @@ class SDP(opcua_device): default_value=[[8192] * 12 * 512] * 16 ) - first_default_settings = [ - # set the masks first, as those filter any subsequent settings - 'TR_fpga_mask_RW' - ] - # ---------- # Attributes # ---------- + version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version()) + FPGA_beamlet_output_enable_R = attribute_wrapper(comms_annotation=["2:FPGA_beamlet_output_enable_R"], datatype=numpy.bool_, dims=(16,)) FPGA_beamlet_output_enable_RW = attribute_wrapper(comms_annotation=["2:FPGA_beamlet_output_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_beamlet_output_hdr_eth_destination_mac_R = attribute_wrapper(comms_annotation=["2:FPGA_beamlet_output_hdr_eth_destination_mac_R"], datatype=numpy.str, dims=(16,)) @@ -161,10 +161,38 @@ class SDP(opcua_device): FPGA_bsn_monitor_input_nof_valid_R = attribute_wrapper(comms_annotation=["2:FPGA_bsn_monitor_input_nof_valid_R"], datatype=numpy.int32, dims=(N_pn,)) FPGA_bsn_monitor_input_nof_err_R = attribute_wrapper(comms_annotation=["2:FPGA_bsn_monitor_input_nof_err_R"], datatype=numpy.int32, dims=(N_pn,)) - # -------- # overloaded functions # -------- + @log_exceptions() + def configure_for_off(self): + """ user code here. is called when the state is set to OFF """ + + # Stop keep-alive + try: + self.OPCua_client.stop() + except Exception as e: + self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e)) + + @log_exceptions() + def configure_for_initialise(self): + """ user code here. is called when the sate is set to INIT """ + """Initialises the attributes and properties of the SDP.""" + + # set up the OPC ua client + self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Fault, self) + + # map an access helper class + for i in self.attr_list(): + try: + i.set_comm_client(self.OPCua_client) + except Exception as e: + # use the pass function instead of setting read/write fails + i.set_pass_func() + self.warn_stream("error while setting the SDP attribute {} read/write function. {}".format(i, e)) + pass + + self.OPCua_client.start() # -------- # Commands @@ -176,7 +204,7 @@ class SDP(opcua_device): def main(args=None, **kwargs): """Main function of the SDP module.""" - from common.lofar_logging import configure_logger + from tangostationcontrol.common.lofar_logging import configure_logger configure_logger() return run((SDP,), args=args, **kwargs) diff --git a/devices/devices/sdp/sst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py similarity index 85% rename from devices/devices/sdp/sst.py rename to tangostationcontrol/tangostationcontrol/devices/sdp/sst.py index 8118656494e79106775dbdec90cecfc4179d248a..1c12330391bd05c28971a3f9ed67cca7e8e66ae9 100644 --- a/devices/devices/sdp/sst.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py @@ -17,11 +17,11 @@ from tango.server import device_property, attribute from tango import AttrWriteType # Additional import -from clients.attribute_wrapper import attribute_wrapper -from clients.statistics_client import StatisticsClient -from clients.opcua_client import OPCUAConnection -from devices.sdp.statistics import Statistics -from devices.sdp.statistics_collector import SSTCollector +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.clients.opcua_client import OPCUAConnection +from tangostationcontrol.clients.statistics_client import StatisticsClient +from tangostationcontrol.devices.sdp.statistics import Statistics +from tangostationcontrol.devices.sdp.statistics_collector import SSTCollector import numpy @@ -50,29 +50,12 @@ class SST(Statistics): mandatory=True ) - FPGA_sst_offload_enable_RW_default = device_property( - dtype='DevVarBooleanArray', - mandatory=False, - default_value=[True] * 16 - ) - FPGA_sst_offload_weighted_subbands_RW_default = device_property( dtype='DevVarBooleanArray', mandatory=False, default_value=[True] * 16 ) - first_default_settings = [ - 'FPGA_sst_offload_hdr_eth_destination_mac_RW', - 'FPGA_sst_offload_hdr_ip_destination_address_RW', - 'FPGA_sst_offload_hdr_udp_destination_port_RW', - - 'FPGA_sst_offload_weighted_subbands_RW', - - # enable only after the offloading is configured correctly - 'FPGA_sst_offload_enable_RW' - ] - # ---------- # Attributes # ---------- @@ -116,7 +99,7 @@ class SST(Statistics): def main(args=None, **kwargs): """Main function of the SST Device module.""" - from common.lofar_logging import configure_logger + from tangostationcontrol.common.lofar_logging import configure_logger configure_logger() return run((SST,), args=args, **kwargs) diff --git a/devices/devices/sdp/statistics.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py similarity index 84% rename from devices/devices/sdp/statistics.py rename to tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py index 0884e5ca7f523af5d56d6a67dd29fd03f9927b5f..abb9bddc91873c6fb793fa3be57fac0127981fbb 100644 --- a/devices/devices/sdp/statistics.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py @@ -16,15 +16,17 @@ from abc import ABCMeta, abstractmethod # PyTango imports from tango.server import device_property, attribute from tango import AttrWriteType + # Additional import import asyncio -from clients.statistics_client import StatisticsClient -from clients.attribute_wrapper import attribute_wrapper - -from devices.opcua_device import opcua_device +from tangostationcontrol.clients.statistics_client import StatisticsClient +from tangostationcontrol.clients.opcua_client import OPCUAConnection +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.devices.hardware_device import hardware_device +from tangostationcontrol.common.lofar_git import get_version +from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions -from common.lofar_logging import device_logging_to_python, log_exceptions import logging logger = logging.getLogger() @@ -33,7 +35,7 @@ import numpy __all__ = ["Statistics"] -class Statistics(opcua_device, metaclass=ABCMeta): +class Statistics(hardware_device, metaclass=ABCMeta): # In derived classes, set this to a subclass of StatisticsCollector @property @@ -45,6 +47,21 @@ class Statistics(opcua_device, metaclass=ABCMeta): # Device Properties # ----------------- + OPC_Server_Name = device_property( + dtype='DevString', + mandatory=True + ) + + OPC_Server_Port = device_property( + dtype='DevULong', + mandatory=True + ) + + OPC_Time_Out = device_property( + dtype='DevDouble', + mandatory=True + ) + Statistics_Client_UDP_Port = device_property( dtype='DevUShort', mandatory=True @@ -59,6 +76,8 @@ class Statistics(opcua_device, metaclass=ABCMeta): # Attributes # ---------- + version_R = attribute(dtype = str, access = AttrWriteType.READ, fget = lambda self: get_version()) + # number of UDP packets and bytes that were received nof_packets_received_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "nof_packets_received"}, datatype=numpy.uint64) nof_bytes_received_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "nof_bytes_received"}, datatype=numpy.uint64) @@ -98,15 +117,16 @@ class Statistics(opcua_device, metaclass=ABCMeta): except Exception as e: logger.exception("Exception while stopping statistics_client in configure_for_off. Exception ignored") - super().configure_for_off() + try: + self.OPCUA_client.stop() + except Exception as e: + logger.exception("Exception while stopping OPC UA connection in configure_for_off. Exception ignored") @log_exceptions() def configure_for_initialise(self): """ user code here. is called when the sate is set to INIT """ """Initialises the attributes and properties of the statistics device.""" - super().configure_for_initialise() - # Options for UDPReceiver udp_options = { "udp_port": self.Statistics_Client_UDP_Port, diff --git a/devices/devices/sdp/statistics_collector.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py similarity index 88% rename from devices/devices/sdp/statistics_collector.py rename to tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py index d9e5668b7e9b3db288a4b2360f4fa298594bbc1c..a073fbd945ce96701d2a19f673141b45a839f449 100644 --- a/devices/devices/sdp/statistics_collector.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py @@ -4,8 +4,8 @@ import logging import numpy from .statistics_packet import SSTPacket, XSTPacket -from common.baselines import nr_baselines, baseline_index, baseline_from_index -from clients.statistics_client_thread import StatisticsClientThread +from tangostationcontrol.common.baselines import nr_baselines, baseline_index, baseline_from_index +from tangostationcontrol.clients.statistics_client_thread import StatisticsClientThread logger = logging.getLogger() @@ -131,8 +131,6 @@ class XSTCollector(StatisticsCollector): # Last value array we've constructed out of the packets "xst_blocks": numpy.zeros((self.MAX_BLOCKS, self.BLOCK_LENGTH * self.BLOCK_LENGTH * self.VALUES_PER_COMPLEX), dtype=numpy.int64), - # Whether the values are actually conjugated and transposed - "xst_conjugated": numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.bool_), "xst_timestamps": numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.float64), "xst_subbands": numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.uint16), "integration_intervals": numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.float32), @@ -164,29 +162,20 @@ class XSTCollector(StatisticsCollector): if fields.first_baseline[antenna] % self.BLOCK_LENGTH != 0: raise ValueError("Packet describes baselines starting at %s, but we require a multiple of BLOCK_LENGTH=%d" % (fields.first_baseline, self.MAX_INPUTS)) - # Make sure we always have a baseline (a,b) with a>=b. If not, we swap the indices and mark that the data must be conjugated and transposed when processed. - first_baseline = fields.first_baseline - if first_baseline[0] < first_baseline[1]: - conjugated = True - first_baseline = (first_baseline[1], first_baseline[0]) - else: - conjugated = False - # the payload contains complex values for the block of baselines of size BLOCK_LENGTH x BLOCK_LENGTH # starting at baseline first_baseline. # # we honour this format, as we want to keep the metadata together with these blocks. we do need to put the blocks in a linear # and tight order, however, so we calculate a block index. - block_index = baseline_index(first_baseline[0] // self.BLOCK_LENGTH, first_baseline[1] // self.BLOCK_LENGTH) - - # We did enough checks on first_baseline for this to be a logic error in our code - assert 0 <= block_index < self.MAX_BLOCKS, f"Received block {block_index}, but have only room for {self.MAX_BLOCKS}. Block starts at baseline {first_baseline}." + block_index = baseline_index(fields.first_baseline[0] // self.BLOCK_LENGTH, fields.first_baseline[1] // self.BLOCK_LENGTH) # process the packet self.parameters["nof_valid_payloads"][fields.gn_index] += numpy.uint64(1) + + block_index = baseline_index(fields.first_baseline[0], fields.first_baseline[1]) + self.parameters["xst_blocks"][block_index][:fields.nof_statistics_per_packet] = fields.payload self.parameters["xst_timestamps"][block_index] = numpy.float64(fields.timestamp().timestamp()) - self.parameters["xst_conjugated"][block_index] = conjugated self.parameters["xst_subbands"][block_index] = numpy.uint16(fields.subband_index) self.parameters["integration_intervals"][block_index] = fields.integration_interval() @@ -195,16 +184,11 @@ class XSTCollector(StatisticsCollector): matrix = numpy.zeros((self.MAX_INPUTS, self.MAX_INPUTS), dtype=numpy.complex64) xst_blocks = self.parameters["xst_blocks"] - xst_conjugated = self.parameters["xst_conjugated"] for block_index in range(self.MAX_BLOCKS): # convert real/imag int to complex float values. this works as real/imag come in pairs block = xst_blocks[block_index].astype(numpy.float32).view(numpy.complex64) - if xst_conjugated[block_index]: - # block is conjugated and transposed. process. - block = block.conjugate().transpose() - # reshape into [a][b] block = block.reshape(self.BLOCK_LENGTH, self.BLOCK_LENGTH) diff --git a/devices/devices/sdp/statistics_packet.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_packet.py similarity index 99% rename from devices/devices/sdp/statistics_packet.py rename to tangostationcontrol/tangostationcontrol/devices/sdp/statistics_packet.py index b68525039c49fdd14b2c47ab8eaadefc97154395..c98ae9b5bdc604e8a55480cc5473e658b10cefa1 100644 --- a/devices/devices/sdp/statistics_packet.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_packet.py @@ -1,6 +1,5 @@ import struct from datetime import datetime, timezone -from typing import Tuple import numpy __all__ = ["StatisticsPacket", "SSTPacket", "XSTPacket", "BSTPacket"] diff --git a/devices/devices/sdp/xst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py similarity index 82% rename from devices/devices/sdp/xst.py rename to tangostationcontrol/tangostationcontrol/devices/sdp/xst.py index 8f3402063b6ada84a0e12b3b09106127d9aa8201..55cd535fe7fdcb9a360f1974c9f5c24cb59066c4 100644 --- a/devices/devices/sdp/xst.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py @@ -15,16 +15,14 @@ from tango.server import run from tango.server import device_property, attribute from tango import AttrWriteType -# Additional import - -from clients.attribute_wrapper import attribute_wrapper -from clients.statistics_client import StatisticsClient -from clients.opcua_client import OPCUAConnection -from common.lofar_logging import device_logging_to_python, log_exceptions +# Additional import +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.clients.opcua_client import OPCUAConnection +from tangostationcontrol.clients.statistics_client import StatisticsClient -from devices.sdp.statistics import Statistics -from devices.sdp.statistics_collector import XSTCollector +from tangostationcontrol.devices.sdp.statistics import Statistics +from tangostationcontrol.devices.sdp.statistics_collector import XSTCollector import numpy @@ -65,37 +63,13 @@ class XST(Statistics): default_value=[[0,102,0,0,0,0,0,0]] * 16 ) - FPGA_xst_integration_interval_RW_default = device_property( - dtype='DevVarDoubleArray', - mandatory=False, - default_value=[1.0] * 16 - ) - - FPGA_xst_offload_enable_RW_default = device_property( - dtype='DevVarBooleanArray', - mandatory=False, - default_value=[True] * 16 - ) - - first_default_settings = [ - 'FPGA_xst_offload_hdr_eth_destination_mac_RW', - 'FPGA_xst_offload_hdr_ip_destination_address_RW', - 'FPGA_xst_offload_hdr_udp_destination_port_RW', - - 'FPGA_xst_subband_select_RW', - 'FPGA_xst_integration_interval_RW', - - # enable only after the offloading is configured correctly - 'FPGA_xst_offload_enable_RW' - ] - # ---------- # Attributes # ---------- # FPGA control points for XSTs - FPGA_xst_integration_interval_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_RW"], datatype=numpy.double, dims=(16,), access=AttrWriteType.READ_WRITE) - FPGA_xst_integration_interval_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_R"], datatype=numpy.double, dims=(16,)) + FPGA_xst_integration_interval_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_RW"], datatype=numpy.double, dims=(8,16), access=AttrWriteType.READ_WRITE) + FPGA_xst_integration_interval_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_R"], datatype=numpy.double, dims=(8,16)) FPGA_xst_offload_enable_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_xst_offload_enable_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_enable_R"], datatype=numpy.bool_, dims=(16,)) FPGA_xst_offload_hdr_eth_destination_mac_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_eth_destination_mac_RW"], datatype=numpy.str, dims=(16,), access=AttrWriteType.READ_WRITE) @@ -115,8 +89,6 @@ class XST(Statistics): nof_payload_errors_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_payload_errors"}, dims=(XSTCollector.MAX_FPGAS,), datatype=numpy.uint64) # latest XSTs xst_blocks_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_blocks"}, dims=(XSTCollector.BLOCK_LENGTH * XSTCollector.BLOCK_LENGTH * XSTCollector.VALUES_PER_COMPLEX, XSTCollector.MAX_BLOCKS), datatype=numpy.int64) - # whether the values in the block are conjugated and transposed - xst_conjugated_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_conjugated"}, dims=(XSTCollector.MAX_BLOCKS,), datatype=numpy.bool_) # reported timestamp for each row in the latest XSTs xst_timestamp_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_timestamps"}, dims=(XSTCollector.MAX_BLOCKS,), datatype=numpy.uint64) # which subband the XSTs describe @@ -156,7 +128,7 @@ class XST(Statistics): def main(args=None, **kwargs): """Main function of the XST Device module.""" - from common.lofar_logging import configure_logger + from tangostationcontrol.common.lofar_logging import configure_logger configure_logger() return run((XST,), args=args, **kwargs) diff --git a/devices/devices/unb2.py b/tangostationcontrol/tangostationcontrol/devices/unb2.py similarity index 82% rename from devices/devices/unb2.py rename to tangostationcontrol/tangostationcontrol/devices/unb2.py index 7ecd96e6f0de4a3d92e9fdf3e66d2fb915aa2303..4216374e913daa3e5d3083753ebb10651ddef61e 100644 --- a/devices/devices/unb2.py +++ b/tangostationcontrol/tangostationcontrol/devices/unb2.py @@ -17,17 +17,32 @@ from tango.server import device_property, attribute from tango import AttrWriteType # Additional import -from clients.attribute_wrapper import attribute_wrapper -from devices.opcua_device import opcua_device +from tangostationcontrol.clients.opcua_client import OPCUAConnection +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.devices.hardware_device import hardware_device -from common.lofar_logging import device_logging_to_python, log_exceptions +from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions +from tangostationcontrol.common.lofar_git import get_version import numpy __all__ = ["UNB2", "main"] @device_logging_to_python() -class UNB2(opcua_device): +class UNB2(hardware_device): + """ + + **Properties:** + + - Device Property + OPC_Server_Name + - Type:'DevString' + OPC_Server_Port + - Type:'DevULong' + OPC_Time_Out + - Type:'DevDouble' + """ + # ----------------- # Device Properties # ----------------- @@ -42,6 +57,8 @@ class UNB2(opcua_device): # Attributes # ---------- + version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version()) + N_unb = 2 N_fpga = 4 N_ddr = 2 @@ -133,10 +150,49 @@ class UNB2(opcua_device): # QualifiedName(2: UNB2_on) # QualifiedName(2: UNB2_off) + @log_exceptions() + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command (a Tango built-in). + """ + self.debug_stream("Shutting down...") + + self.Off() + self.debug_stream("Shut down. Good bye.") # -------- # overloaded functions # -------- + @log_exceptions() + def configure_for_off(self): + """ user code here. is called when the state is set to OFF """ + # Stop keep-alive + try: + self.opcua_connection.stop() + except Exception as e: + self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e)) + + @log_exceptions() + def configure_for_initialise(self): + """ user code here. is called when the sate is set to INIT """ + """Initialises the attributes and properties of theRECV.""" + + # set up the OPC ua client + self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Fault, self) + + # map an access helper class + for i in self.attr_list(): + try: + i.set_comm_client(self.OPCua_client) + except Exception as e: + # use the pass function instead of setting read/write fails + i.set_pass_func() + self.warn_stream("error while setting the UNB2 attribute {} read/write function. {}".format(i, e)) + + self.OPCua_client.start() # -------- # Commands @@ -148,7 +204,7 @@ class UNB2(opcua_device): def main(args=None, **kwargs): """Main function of the UNB2 module.""" - from common.lofar_logging import configure_logger + from tangostationcontrol.common.lofar_logging import configure_logger configure_logger() return run((UNB2,), args=args, **kwargs) diff --git a/devices/examples/HW_device_template.py b/tangostationcontrol/tangostationcontrol/examples/HW_device_template.py similarity index 94% rename from devices/examples/HW_device_template.py rename to tangostationcontrol/tangostationcontrol/examples/HW_device_template.py index c51538c9db16b0894a74a9f149a715ede94a8acb..4645488405ac1dc23acde36a7659e3b9182c57a7 100644 --- a/devices/examples/HW_device_template.py +++ b/tangostationcontrol/tangostationcontrol/examples/HW_device_template.py @@ -11,11 +11,9 @@ # PyTango imports from tango.server import run -from tango import AttrWriteType # Additional import -from clients.attribute_wrapper import attribute_wrapper -from devices.hardware_device import hardware_device +from tangostationcontrol.devices.hardware_device import hardware_device __all__ = ["HW_dev"] diff --git a/devices/integration_test/client/__init__.py b/tangostationcontrol/tangostationcontrol/examples/__init__.py similarity index 100% rename from devices/integration_test/client/__init__.py rename to tangostationcontrol/tangostationcontrol/examples/__init__.py diff --git a/devices/integration_test/devices/__init__.py b/tangostationcontrol/tangostationcontrol/examples/load_from_disk/__init__.py similarity index 100% rename from devices/integration_test/devices/__init__.py rename to tangostationcontrol/tangostationcontrol/examples/load_from_disk/__init__.py diff --git a/devices/examples/load_from_disk/ini_client.py b/tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_client.py similarity index 98% rename from devices/examples/load_from_disk/ini_client.py rename to tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_client.py index cd227f23458c672c08b3acf08ba65fa9a48b581d..82c57a685e105527625c7dd3c0acdb77e59e1c8c 100644 --- a/devices/examples/load_from_disk/ini_client.py +++ b/tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_client.py @@ -1,4 +1,4 @@ -from clients.comms_client import CommClient +from tangostationcontrol.clients.comms_client import CommClient import configparser import numpy diff --git a/devices/examples/load_from_disk/ini_device.py b/tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_device.py similarity index 96% rename from devices/examples/load_from_disk/ini_device.py rename to tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_device.py index 8391d2823b68be53e56bd6f8b650b0f46362f429..1f65d3ccd5f0827305a2d1a91e958b6e6241bc85 100644 --- a/devices/examples/load_from_disk/ini_device.py +++ b/tangostationcontrol/tangostationcontrol/examples/load_from_disk/ini_device.py @@ -12,16 +12,14 @@ # PyTango imports from tango.server import run from tango import AttrWriteType -# Additional import -from clients.attribute_wrapper import attribute_wrapper -from devices.hardware_device import hardware_device - import configparser import numpy -from ini_client import * - +# Additional import +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.devices.hardware_device import hardware_device +from tangostationcontrol.examples.load_from_disk.ini_client import * __all__ = ["ini_device"] diff --git a/devices/test/__init__.py b/tangostationcontrol/tangostationcontrol/examples/snmp/__init__.py similarity index 100% rename from devices/test/__init__.py rename to tangostationcontrol/tangostationcontrol/examples/snmp/__init__.py diff --git a/devices/examples/snmp/snmp.py b/tangostationcontrol/tangostationcontrol/examples/snmp/snmp.py similarity index 91% rename from devices/examples/snmp/snmp.py rename to tangostationcontrol/tangostationcontrol/examples/snmp/snmp.py index 9ef61ada5a88ae8b9a19447c5cdcf2b42c21149e..845ff0937ccaaf3ad120fe6c96acfce581417f0f 100644 --- a/devices/examples/snmp/snmp.py +++ b/tangostationcontrol/tangostationcontrol/examples/snmp/snmp.py @@ -17,9 +17,9 @@ from tango.server import device_property from tango import AttrWriteType # Additional import -from examples.snmp.snmp_client import SNMP_client -from clients.attribute_wrapper import attribute_wrapper -from devices.hardware_device import hardware_device +from tangostationcontrol.examples.snmp.snmp_client import SNMP_client +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper +from tangostationcontrol.devices.hardware_device import hardware_device import numpy @@ -111,8 +111,7 @@ class SNMP(hardware_device): def main(args=None, **kwargs): """Main function of the module.""" - from common.lofar_logging import configure_logger - import logging - configure_logger(logging.getLogger()) + from tangostationcontrol.common.lofar_logging import configure_logger + configure_logger() return run((SNMP,), args=args, **kwargs) diff --git a/devices/examples/snmp/snmp_client.py b/tangostationcontrol/tangostationcontrol/examples/snmp/snmp_client.py similarity index 98% rename from devices/examples/snmp/snmp_client.py rename to tangostationcontrol/tangostationcontrol/examples/snmp/snmp_client.py index 96ac67140b9bdbdba7ab4d4fb8651b5e9674c219..00464f37d92800207fec524279126e494886d01a 100644 --- a/devices/examples/snmp/snmp_client.py +++ b/tangostationcontrol/tangostationcontrol/examples/snmp/snmp_client.py @@ -1,5 +1,5 @@ -from clients.comms_client import CommClient +from tangostationcontrol.clients.comms_client import CommClient import snmp diff --git a/devices/integration_test/README.md b/tangostationcontrol/tangostationcontrol/integration_test/README.md similarity index 100% rename from devices/integration_test/README.md rename to tangostationcontrol/tangostationcontrol/integration_test/README.md diff --git a/devices/test/clients/__init__.py b/tangostationcontrol/tangostationcontrol/integration_test/__init__.py similarity index 100% rename from devices/test/clients/__init__.py rename to tangostationcontrol/tangostationcontrol/integration_test/__init__.py diff --git a/devices/integration_test/base.py b/tangostationcontrol/tangostationcontrol/integration_test/base.py similarity index 85% rename from devices/integration_test/base.py rename to tangostationcontrol/tangostationcontrol/integration_test/base.py index 241f0ecd409fd16484d81e31f1e1f83dc1b9d81b..3583d1901a3ae7cecfefee1ac6ad698f0c098456 100644 --- a/devices/integration_test/base.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/base.py @@ -7,15 +7,10 @@ # Distributed under the terms of the APACHE license. # See LICENSE.txt for more info. -from common.lofar_logging import configure_logger - import unittest import asynctest import testscenarios -"""Setup logging for integration tests""" -configure_logger(debug=True) - class BaseIntegrationTestCase(testscenarios.WithScenarios, unittest.TestCase): """Integration test base class.""" diff --git a/devices/test/common/__init__.py b/tangostationcontrol/tangostationcontrol/integration_test/client/__init__.py similarity index 100% rename from devices/test/common/__init__.py rename to tangostationcontrol/tangostationcontrol/integration_test/client/__init__.py diff --git a/devices/integration_test/client/test_sdptr_sim.py b/tangostationcontrol/tangostationcontrol/integration_test/client/test_sdptr_sim.py similarity index 92% rename from devices/integration_test/client/test_sdptr_sim.py rename to tangostationcontrol/tangostationcontrol/integration_test/client/test_sdptr_sim.py index ab9288b727e515c19b07c99d1fe8a233d7032055..a09f407e2982d7b873021f03b1eb9e78fe336e44 100644 --- a/devices/integration_test/client/test_sdptr_sim.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/client/test_sdptr_sim.py @@ -9,7 +9,7 @@ from asyncua import Client -from integration_test import base +from tangostationcontrol.integration_test import base class TestSDPTRSim(base.IntegrationAsyncTestCase): diff --git a/devices/integration_test/client/test_tcp_replicator.py b/tangostationcontrol/tangostationcontrol/integration_test/client/test_tcp_replicator.py similarity index 97% rename from devices/integration_test/client/test_tcp_replicator.py rename to tangostationcontrol/tangostationcontrol/integration_test/client/test_tcp_replicator.py index ca45c4c52ab7f5e379c484b964a05225950fc9e1..2467fd6dd8a7b24cc786c5b2cc10a25a610b88f1 100644 --- a/devices/integration_test/client/test_tcp_replicator.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/client/test_tcp_replicator.py @@ -7,18 +7,16 @@ # Distributed under the terms of the APACHE license. # See LICENSE.txt for more info. -from asyncio import Queue - import logging import time import socket import sys -from clients.tcp_replicator import TCPReplicator +import timeout_decorator -from integration_test import base +from tangostationcontrol.clients.tcp_replicator import TCPReplicator -import timeout_decorator +from tangostationcontrol.integration_test import base logger = logging.getLogger() diff --git a/devices/integration_test/client/test_unb2_sim.py b/tangostationcontrol/tangostationcontrol/integration_test/client/test_unb2_sim.py similarity index 92% rename from devices/integration_test/client/test_unb2_sim.py rename to tangostationcontrol/tangostationcontrol/integration_test/client/test_unb2_sim.py index 1eb9972400f2121a6365b2fcb875ecbc2190cdff..5d2e1016e4edfc1f7869b20dafbb5d0a01a46bf2 100644 --- a/devices/integration_test/client/test_unb2_sim.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/client/test_unb2_sim.py @@ -9,7 +9,7 @@ from asyncua import Client -from integration_test import base +from tangostationcontrol.integration_test import base class TestUNB2Sim(base.IntegrationAsyncTestCase): diff --git a/devices/test/devices/__init__.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/__init__.py similarity index 100% rename from devices/test/devices/__init__.py rename to tangostationcontrol/tangostationcontrol/integration_test/devices/__init__.py diff --git a/devices/integration_test/devices/test_device_recv.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_recv.py similarity index 96% rename from devices/integration_test/devices/test_device_recv.py rename to tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_recv.py index 3a010a000c03d3c039f8f93a68c0f6437bc30db1..285f02244d490f97d36be678c5628447e64d96c4 100644 --- a/devices/integration_test/devices/test_device_recv.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_recv.py @@ -7,12 +7,10 @@ # Distributed under the terms of the APACHE license. # See LICENSE.txt for more info. -import time - from tango import DeviceProxy from tango._tango import DevState -from integration_test import base +from tangostationcontrol.integration_test import base class TestDeviceRECV(base.IntegrationTestCase): diff --git a/devices/integration_test/devices/test_device_sdp.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sdp.py similarity index 96% rename from devices/integration_test/devices/test_device_sdp.py rename to tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sdp.py index 5f064128f858e0bd2c44768a4f13057e5dc20266..b4dbb02c0f6d25f4f37cddf687f2b6da47a3738c 100644 --- a/devices/integration_test/devices/test_device_sdp.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sdp.py @@ -7,12 +7,10 @@ # Distributed under the terms of the APACHE license. # See LICENSE.txt for more info. -import time - from tango import DeviceProxy from tango._tango import DevState -from integration_test import base +from tangostationcontrol.integration_test import base class TestDeviceSDP(base.IntegrationTestCase): diff --git a/devices/integration_test/devices/test_device_sst.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sst.py similarity index 98% rename from devices/integration_test/devices/test_device_sst.py rename to tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sst.py index a6b71d328305f2dafed46f9e4f3ea9209df9601d..7c20dc1cc96b5c87c6a1f2dc6e3cc5ef10fd2c13 100644 --- a/devices/integration_test/devices/test_device_sst.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_sst.py @@ -13,7 +13,7 @@ import time from tango import DeviceProxy from tango._tango import DevState -from integration_test import base +from tangostationcontrol.integration_test import base class TestDeviceSST(base.IntegrationTestCase): diff --git a/devices/integration_test/devices/test_device_unb2.py b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_unb2.py similarity index 92% rename from devices/integration_test/devices/test_device_unb2.py rename to tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_unb2.py index 97f31ab6ee162f8183db963e92f4f03b9ee7f617..e0196230b38782ba5003dd0ff46d1fd51ede531b 100644 --- a/devices/integration_test/devices/test_device_unb2.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/devices/test_device_unb2.py @@ -7,12 +7,10 @@ # Distributed under the terms of the APACHE license. # See LICENSE.txt for more info. -import time - from tango import DeviceProxy from tango._tango import DevState -from integration_test import base +from tangostationcontrol.integration_test import base class TestDeviceUNB2(base.IntegrationTestCase): diff --git a/devices/statistics_writer/README.md b/tangostationcontrol/tangostationcontrol/statistics_writer/README.md similarity index 100% rename from devices/statistics_writer/README.md rename to tangostationcontrol/tangostationcontrol/statistics_writer/README.md diff --git a/tangostationcontrol/tangostationcontrol/statistics_writer/__init__.py b/tangostationcontrol/tangostationcontrol/statistics_writer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/devices/statistics_writer/hdf5_writer.py b/tangostationcontrol/tangostationcontrol/statistics_writer/hdf5_writer.py similarity index 94% rename from devices/statistics_writer/hdf5_writer.py rename to tangostationcontrol/tangostationcontrol/statistics_writer/hdf5_writer.py index 6715dd870608a0202610ea52c417695844f0d1c9..87fb3d28d7ad991d9ff8e6442656835e6a636fb9 100644 --- a/devices/statistics_writer/hdf5_writer.py +++ b/tangostationcontrol/tangostationcontrol/statistics_writer/hdf5_writer.py @@ -11,8 +11,8 @@ import logging # import statistics classes with workaround import sys sys.path.append("..") -from devices.sdp.statistics_packet import SSTPacket, XSTPacket, BSTPacket, StatisticsPacket -import devices.sdp.statistics_collector as statistics_collector +from tangostationcontrol.devices.sdp.statistics_packet import SSTPacket, XSTPacket, BSTPacket, StatisticsPacket +import tangostationcontrol.devices.sdp.statistics_collector as statistics_collector logger = logging.getLogger("statistics_writer") @@ -133,7 +133,7 @@ class hdf5_writer: """ # create the new hdf5 group based on the timestamp of packets - current_group = self.file.create_group("{}_{}".format(self.mode, self.current_timestamp.isoformat(timespec="milliseconds"))) + current_group = self.file.create_group("{}_{}".format(self.mode, self.current_timestamp.strftime("%Y-%m-%d-%H-%M-%S-%f")[:-3])) # store the statistics values for the current group self.store_function(current_group) @@ -158,11 +158,11 @@ class hdf5_writer: def write_sst_matrix(self, current_group): # store the SST values - current_group.create_dataset(name="values", data=self.current_matrix.parameters["sst_values"].astype(numpy.float32), compression="gzip") + current_group.create_dataset(name="sst_values", data=self.current_matrix.parameters["sst_values"].astype(numpy.float32), compression="gzip") def write_xst_matrix(self, current_group): # requires a function call to transform the xst_blocks in to the right structure - current_group.create_dataset(name="values", data=self.current_matrix.xst_values().astype(numpy.cfloat), compression="gzip") + current_group.create_dataset(name="xst_values", data=self.current_matrix.xst_values().astype(numpy.cfloat), compression="gzip") def write_bst_matrix(self, current_group): raise NotImplementedError("BST values not implemented") diff --git a/devices/statistics_writer/receiver.py b/tangostationcontrol/tangostationcontrol/statistics_writer/receiver.py similarity index 95% rename from devices/statistics_writer/receiver.py rename to tangostationcontrol/tangostationcontrol/statistics_writer/receiver.py index 919357764a2196cb7955e4ec77f2487b81d24d59..e719eef9ae4ec888a99a7a1dc5b831e5dba74915 100644 --- a/devices/statistics_writer/receiver.py +++ b/tangostationcontrol/tangostationcontrol/statistics_writer/receiver.py @@ -2,7 +2,7 @@ import socket import sys sys.path.append("..") -from devices.sdp.statistics_packet import StatisticsPacket +from tangostationcontrol.devices.sdp.statistics_packet import StatisticsPacket import os class receiver: diff --git a/devices/statistics_writer/statistics_writer.py b/tangostationcontrol/tangostationcontrol/statistics_writer/statistics_writer.py similarity index 99% rename from devices/statistics_writer/statistics_writer.py rename to tangostationcontrol/tangostationcontrol/statistics_writer/statistics_writer.py index 594e261c6d1e00e0ea7882c595449813c305c8ce..e2d4666fd581b01cdb99e9ad717fbccd32cfa33c 100644 --- a/devices/statistics_writer/statistics_writer.py +++ b/tangostationcontrol/tangostationcontrol/statistics_writer/statistics_writer.py @@ -70,3 +70,5 @@ if __name__ == "__main__": logger.info("End of input.") finally: writer.close_writer() + + diff --git a/tangostationcontrol/tangostationcontrol/statistics_writer/test/__init__.py b/tangostationcontrol/tangostationcontrol/statistics_writer/test/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/devices/statistics_writer/test/devices_test_SDP_SST_statistics_packets.bin b/tangostationcontrol/tangostationcontrol/statistics_writer/test/devices_test_SDP_SST_statistics_packets.bin similarity index 100% rename from devices/statistics_writer/test/devices_test_SDP_SST_statistics_packets.bin rename to tangostationcontrol/tangostationcontrol/statistics_writer/test/devices_test_SDP_SST_statistics_packets.bin diff --git a/tangostationcontrol/tangostationcontrol/statistics_writer/test/hdf5_explorer.py b/tangostationcontrol/tangostationcontrol/statistics_writer/test/hdf5_explorer.py new file mode 100644 index 0000000000000000000000000000000000000000..102c36b79f7beeb6a34ffba9b95a495a85a76f6e --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/statistics_writer/test/hdf5_explorer.py @@ -0,0 +1,95 @@ +import h5py +import numpy + +import argparse + +parser = argparse.ArgumentParser(description='Select a file to explore') +parser.add_argument('--file', type=str, help='the name and path of the file') + +import logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("hdf5_explorer") +logger.setLevel(logging.DEBUG) + + +class statistics_data: + """ + Example class not used by anything + This class takes the file and the statistics name as its __init__ arguments and then stores the + the datasets in them. + """ + +class explorer: + """ + This class serves both as a tool to test and verify the content of HDF5 files as well as provide an example + of how you can go through HDF5 files. + """ + + + def __init__(self, filename): + self.file = h5py.File(filename, 'r') + + def print_all_statistics_full(self): + """ + Explores the file with knowledge of the file structure. assumes all top level groups are statistics + and that all statistics groups are made up of datasets. + Prints the groups, the datasets and the content of the datasets. + + Can easily be modified to instead of just logging all the data, store it in whatever structure is needed. + """ + + for group_key in self.file.keys(): + dataset = list(self.file[group_key]) + + #print group name + logger.debug(f" \n\ngroup: {group_key}") + + # Go through all the datasets + for i in dataset: + data = self.file.get(f"{group_key}/{i}") + logger.debug(f" dataset: {i}") + logger.debug(f" Data: {numpy.array(data)}") + + # go through all the attributes in the group (This is the header info) + attr_keys = self.file[group_key].attrs.keys() + for i in attr_keys: + attr = self.file[group_key].attrs[i] + + logger.debug(f" {i}: {attr}") + + def print_all_statistics_top_level(self): + """ + Explores the file with knowledge of the file structure. assumes all top level groups are statistics + and that all statistics groups are made up of datasets. + This function prints only the top level groups, AKA all the statistics collected. Useful when dealing with + potentially hundreds of statistics. + """ + # List all groups + logger.debug("Listing all statistics stored in this file:") + + for group_key in self.file.keys(): + logger.debug(group_key) + + +# create a data dumper that creates a new file every 10s (for testing) +if __name__ == "__main__": + args = parser.parse_args() + Explorer = explorer(args.file) + + """ + Print the entire files content + """ + Explorer.print_all_statistics_full() + + """ + Print only the names of all the statistics in this file + """ + logger.debug("--------------Top level groups--------------") + Explorer.print_all_statistics_top_level() + + + + + + + diff --git a/devices/statistics_writer/test/test_server.py b/tangostationcontrol/tangostationcontrol/statistics_writer/test/test_server.py similarity index 100% rename from devices/statistics_writer/test/test_server.py rename to tangostationcontrol/tangostationcontrol/statistics_writer/test/test_server.py diff --git a/tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/__init__.py b/tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/devices/statistics_writer/udp_dev/udp_client.py b/tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_client.py similarity index 100% rename from devices/statistics_writer/udp_dev/udp_client.py rename to tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_client.py diff --git a/devices/statistics_writer/udp_dev/udp_server.py b/tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_server.py similarity index 100% rename from devices/statistics_writer/udp_dev/udp_server.py rename to tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_server.py diff --git a/devices/statistics_writer/udp_dev/udp_write_manager.py b/tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_write_manager.py similarity index 100% rename from devices/statistics_writer/udp_dev/udp_write_manager.py rename to tangostationcontrol/tangostationcontrol/statistics_writer/udp_dev/udp_write_manager.py diff --git a/devices/test/README.md b/tangostationcontrol/tangostationcontrol/test/README.md similarity index 100% rename from devices/test/README.md rename to tangostationcontrol/tangostationcontrol/test/README.md diff --git a/devices/test/SDP_SST_statistics_packet.bin b/tangostationcontrol/tangostationcontrol/test/SDP_SST_statistics_packet.bin similarity index 100% rename from devices/test/SDP_SST_statistics_packet.bin rename to tangostationcontrol/tangostationcontrol/test/SDP_SST_statistics_packet.bin diff --git a/devices/test/SDP_SST_statistics_packets.bin b/tangostationcontrol/tangostationcontrol/test/SDP_SST_statistics_packets.bin similarity index 100% rename from devices/test/SDP_SST_statistics_packets.bin rename to tangostationcontrol/tangostationcontrol/test/SDP_SST_statistics_packets.bin diff --git a/devices/test/SDP_XST_statistics_packets.bin b/tangostationcontrol/tangostationcontrol/test/SDP_XST_statistics_packets.bin similarity index 100% rename from devices/test/SDP_XST_statistics_packets.bin rename to tangostationcontrol/tangostationcontrol/test/SDP_XST_statistics_packets.bin diff --git a/tangostationcontrol/tangostationcontrol/test/__init__.py b/tangostationcontrol/tangostationcontrol/test/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/devices/test/base.py b/tangostationcontrol/tangostationcontrol/test/base.py similarity index 84% rename from devices/test/base.py rename to tangostationcontrol/tangostationcontrol/test/base.py index 66e64ea9a8669713f672db2088344d96a17f6e7c..81a76c46e843dd7af91f19e1c142f612916157c7 100644 --- a/devices/test/base.py +++ b/tangostationcontrol/tangostationcontrol/test/base.py @@ -7,15 +7,10 @@ # Distributed under the terms of the APACHE license. # See LICENSE.txt for more info. -from common.lofar_logging import configure_logger - import unittest import testscenarios import asynctest -"""Setup logging for unit tests""" -configure_logger(debug=True) - class BaseTestCase(testscenarios.WithScenarios, unittest.TestCase): """Test base class.""" diff --git a/tangostationcontrol/tangostationcontrol/test/clients/__init__.py b/tangostationcontrol/tangostationcontrol/test/clients/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/devices/test/clients/test_attr_wrapper.py b/tangostationcontrol/tangostationcontrol/test/clients/test_attr_wrapper.py similarity index 99% rename from devices/test/clients/test_attr_wrapper.py rename to tangostationcontrol/tangostationcontrol/test/clients/test_attr_wrapper.py index 8711d989a67730667c10aed91de7c9929c500fcb..f1c5b652c545b31f897388ddce0c8f66df37912e 100644 --- a/devices/test/clients/test_attr_wrapper.py +++ b/tangostationcontrol/tangostationcontrol/test/clients/test_attr_wrapper.py @@ -10,13 +10,13 @@ from tango import DevState # Internal imports -from test.clients.test_client import test_client -from clients.attribute_wrapper import * -from devices.hardware_device import * +from tangostationcontrol.test.clients.test_client import test_client +from tangostationcontrol.clients.attribute_wrapper import * +from tangostationcontrol.devices.hardware_device import * # Test imports from tango.test_context import DeviceTestContext -from test import base +from tangostationcontrol.test import base import asyncio diff --git a/devices/test/clients/test_client.py b/tangostationcontrol/tangostationcontrol/test/clients/test_client.py similarity index 98% rename from devices/test/clients/test_client.py rename to tangostationcontrol/tangostationcontrol/test/clients/test_client.py index 039974a1e34ae1a0c9779fd29c2c87f545bc38b7..a17561d77b7c6d05a06683a8b7a1cf023a289dcf 100644 --- a/devices/test/clients/test_client.py +++ b/tangostationcontrol/tangostationcontrol/test/clients/test_client.py @@ -3,7 +3,7 @@ import numpy # Test imports -from clients.comms_client import CommClient +from tangostationcontrol.clients.comms_client import CommClient class test_client(CommClient): diff --git a/devices/test/clients/test_opcua_client.py b/tangostationcontrol/tangostationcontrol/test/clients/test_opcua_client.py similarity index 98% rename from devices/test/clients/test_opcua_client.py rename to tangostationcontrol/tangostationcontrol/test/clients/test_opcua_client.py index 6315abb20547cb35233b9b47f8f8c32414b1159e..b1e73001328da7bd4681529c408b1514a143262b 100644 --- a/devices/test/clients/test_opcua_client.py +++ b/tangostationcontrol/tangostationcontrol/test/clients/test_opcua_client.py @@ -1,16 +1,16 @@ import numpy -from clients.opcua_client import OPCUAConnection -from clients import opcua_client - import asyncua import io import asyncio from unittest import mock -import unittest from test import base import asynctest +from tangostationcontrol.clients.opcua_client import OPCUAConnection +from tangostationcontrol.clients import opcua_client + +from tangostationcontrol.test import base class attr_props: diff --git a/devices/test/clients/test_statistics_client_thread.py b/tangostationcontrol/tangostationcontrol/test/clients/test_statistics_client_thread.py similarity index 86% rename from devices/test/clients/test_statistics_client_thread.py rename to tangostationcontrol/tangostationcontrol/test/clients/test_statistics_client_thread.py index fd7ce0701f9d792863909b9f8ee4a9d39a2b1fd1..17f866871bd682b3f289364c16a55e5ee2010a7c 100644 --- a/devices/test/clients/test_statistics_client_thread.py +++ b/tangostationcontrol/tangostationcontrol/test/clients/test_statistics_client_thread.py @@ -10,9 +10,10 @@ import logging from unittest import mock -from clients.statistics_client_thread import StatisticsClientThread +from tangostationcontrol.clients.statistics_client_thread import \ + StatisticsClientThread -from test import base +from tangostationcontrol.test import base logger = logging.getLogger() diff --git a/devices/test/clients/test_tcp_replicator.py b/tangostationcontrol/tangostationcontrol/test/clients/test_tcp_replicator.py similarity index 97% rename from devices/test/clients/test_tcp_replicator.py rename to tangostationcontrol/tangostationcontrol/test/clients/test_tcp_replicator.py index a9babed0eb7af7a58544b3ff7535c3113ed12ca3..04c87f1d5a15705f7d0b8ce1460141255cb02b27 100644 --- a/devices/test/clients/test_tcp_replicator.py +++ b/tangostationcontrol/tangostationcontrol/test/clients/test_tcp_replicator.py @@ -9,15 +9,14 @@ import logging import time -from queue import Queue from unittest import mock -from clients.tcp_replicator import TCPReplicator -from clients import tcp_replicator +import timeout_decorator -from test import base +from tangostationcontrol.clients.tcp_replicator import TCPReplicator +from tangostationcontrol.clients import tcp_replicator -import timeout_decorator +from tangostationcontrol.test import base logger = logging.getLogger() diff --git a/tangostationcontrol/tangostationcontrol/test/common/__init__.py b/tangostationcontrol/tangostationcontrol/test/common/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/devices/test/common/test_baselines.py b/tangostationcontrol/tangostationcontrol/test/common/test_baselines.py similarity index 91% rename from devices/test/common/test_baselines.py rename to tangostationcontrol/tangostationcontrol/test/common/test_baselines.py index 206b4ca0eccefe1012519c8236d158e52f1cdc38..25eb5d1dfffa2fca8748d74020893edfb17c2037 100644 --- a/devices/test/common/test_baselines.py +++ b/tangostationcontrol/tangostationcontrol/test/common/test_baselines.py @@ -7,9 +7,9 @@ # Distributed under the terms of the APACHE license. # See LICENSE.txt for more info. -from common import baselines +from tangostationcontrol.common import baselines -from test import base +from tangostationcontrol.test import base class TestBaselines(base.TestCase): diff --git a/devices/test/common/test_lofar_git.py b/tangostationcontrol/tangostationcontrol/test/common/test_lofar_git.py similarity index 97% rename from devices/test/common/test_lofar_git.py rename to tangostationcontrol/tangostationcontrol/test/common/test_lofar_git.py index 52a1c7d876fc2827757f082e0f44a0a64b1ffc78..295cf0141064a6d23eec9aa101234d87160c4937 100644 --- a/devices/test/common/test_lofar_git.py +++ b/tangostationcontrol/tangostationcontrol/test/common/test_lofar_git.py @@ -10,9 +10,9 @@ import git from unittest import mock -from common import lofar_git +from tangostationcontrol.common import lofar_git -from test import base +from tangostationcontrol.test import base class TestLofarGit(base.TestCase): diff --git a/devices/test/common/test_lofar_logging.py b/tangostationcontrol/tangostationcontrol/test/common/test_lofar_logging.py similarity index 97% rename from devices/test/common/test_lofar_logging.py rename to tangostationcontrol/tangostationcontrol/test/common/test_lofar_logging.py index 534b2650c8d432ef7c7dae9e32448b01cc5913f3..30a4faa603b4e2550c8c26a13958119a225de8c3 100644 --- a/devices/test/common/test_lofar_logging.py +++ b/tangostationcontrol/tangostationcontrol/test/common/test_lofar_logging.py @@ -7,15 +7,15 @@ # Distributed under the terms of the APACHE license. # See LICENSE.txt for more info. -import git from unittest import mock - -from common import lofar_logging import logging + from tango.server import Device from tango import device_server -from test import base +from tangostationcontrol.common import lofar_logging + +from tangostationcontrol.test import base class TestLofarLogging(base.TestCase): diff --git a/tangostationcontrol/tangostationcontrol/test/devices/__init__.py b/tangostationcontrol/tangostationcontrol/test/devices/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/devices/test/devices/random_data.py b/tangostationcontrol/tangostationcontrol/test/devices/random_data.py similarity index 100% rename from devices/test/devices/random_data.py rename to tangostationcontrol/tangostationcontrol/test/devices/random_data.py diff --git a/devices/test/devices/test_abstract_device.py b/tangostationcontrol/tangostationcontrol/test/devices/test_abstract_device.py similarity index 95% rename from devices/test/devices/test_abstract_device.py rename to tangostationcontrol/tangostationcontrol/test/devices/test_abstract_device.py index f54383c9a1b85f5c9e442f51d7f04d061951f772..469ea19fa970cf48c2de9c4c6b5648bd7b756040 100644 --- a/devices/test/devices/test_abstract_device.py +++ b/tangostationcontrol/tangostationcontrol/test/devices/test_abstract_device.py @@ -16,9 +16,9 @@ from tango.server import attribute from tango.test_context import DeviceTestContext -from devices.abstract_device import AbstractDeviceMetas +from tangostationcontrol.devices.abstract_device import AbstractDeviceMetas -from test import base +from tangostationcontrol.test import base class TestAbstractDevice(base.TestCase): diff --git a/devices/test/devices/test_statistics_collector.py b/tangostationcontrol/tangostationcontrol/test/devices/test_statistics_collector.py similarity index 58% rename from devices/test/devices/test_statistics_collector.py rename to tangostationcontrol/tangostationcontrol/test/devices/test_statistics_collector.py index 5fe4e24dabbf169664b19250cba13f19b8020327..3266dbd148bb4872ec1f2eec8f9c555414fde16c 100644 --- a/devices/test/devices/test_statistics_collector.py +++ b/tangostationcontrol/tangostationcontrol/test/devices/test_statistics_collector.py @@ -1,22 +1,19 @@ -from devices.sdp.statistics_collector import XSTCollector -from devices.sdp.statistics_packet import XSTPacket +from tangostationcontrol.devices.sdp.statistics_collector import XSTCollector +from tangostationcontrol.devices.sdp.statistics_packet import XSTPacket -from test import base +from tangostationcontrol.test import base class TestXSTCollector(base.TestCase): def test_valid_packet(self): collector = XSTCollector() - # a valid packet as obtained from SDP, with 64-bit BE 1+1j as payload at (12,0) - packet = b'X\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x0c\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01' + # a valid packet as obtained from SDP, with 64-bit BE 1+1j as payload + packet = b'X\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01' # parse it ourselves to extract info nicely fields = XSTPacket(packet) fpga_index = fields.gn_index - # baseline indeed should be (12,0) - self.assertEqual((12,0), fields.first_baseline) - # this should not throw collector.process_packet(packet) @@ -44,51 +41,10 @@ class TestXSTCollector(base.TestCase): else: self.assertEqual(0+0j, xst_values[baseline_a][baseline_b], msg=f'element [{baseline_a}][{baseline_b}] was not in packet, but was written to the XST matrix.') - def test_conjugated_packet(self): - """ Test whether a packet with a baseline (a,b) with a<b will get its payload conjugated. """ - - collector = XSTCollector() - - # a valid packet as obtained from SDP, with 64-bit BE 1+1j as payload, at baseline (0,12) - # VV VV - packet = b'X\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x00\x0c\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01' - - # parse it ourselves to extract info nicely - fields = XSTPacket(packet) - - # baseline indeed should be (0,12) - self.assertEqual((0,12), fields.first_baseline) - - # this should not throw - collector.process_packet(packet) - - # counters should now be updated - self.assertEqual(1, collector.parameters["nof_packets"]) - self.assertEqual(0, collector.parameters["nof_invalid_packets"]) - - # check whether the data ended up in the right block, and the rest is still zero - xst_values = collector.xst_values() - - for baseline_a in range(collector.MAX_INPUTS): - for baseline_b in range(collector.MAX_INPUTS): - if baseline_b > baseline_a: - # only scan top-left triangle - continue - - # use swapped indices! - baseline_a_was_in_packet = (fields.first_baseline[1] <= baseline_a < fields.first_baseline[1] + fields.nof_signal_inputs) - baseline_b_was_in_packet = (fields.first_baseline[0] <= baseline_b < fields.first_baseline[0] + fields.nof_signal_inputs) - - if baseline_a_was_in_packet and baseline_b_was_in_packet: - self.assertEqual(1-1j, xst_values[baseline_a][baseline_b], msg=f'element [{baseline_a}][{baseline_b}] did not end up conjugated in XST matrix.') - else: - self.assertEqual(0+0j, xst_values[baseline_a][baseline_b], msg=f'element [{baseline_a}][{baseline_b}] was not in packet, but was written to the XST matrix.') - def test_invalid_packet(self): collector = XSTCollector() # an invalid packet - # V packet = b'S\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01' # this should throw @@ -106,7 +62,6 @@ class TestXSTCollector(base.TestCase): collector = XSTCollector() # an valid packet with a payload error - # V packet = b'X\x05\x00\x00\x00\x00\x00\x00\x14\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01' # parse it ourselves to extract info nicely diff --git a/devices/toolkit/README.md b/tangostationcontrol/tangostationcontrol/toolkit/README.md similarity index 100% rename from devices/toolkit/README.md rename to tangostationcontrol/tangostationcontrol/toolkit/README.md diff --git a/tangostationcontrol/tangostationcontrol/toolkit/__init__.py b/tangostationcontrol/tangostationcontrol/toolkit/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/devices/toolkit/archiver.py b/tangostationcontrol/tangostationcontrol/toolkit/archiver.py similarity index 99% rename from devices/toolkit/archiver.py rename to tangostationcontrol/tangostationcontrol/toolkit/archiver.py index 0991b05c6fac220a786b90bc922032f130785c12..a21c48268eac7d76ea772c579b2c87697f5b7995 100644 --- a/devices/toolkit/archiver.py +++ b/tangostationcontrol/tangostationcontrol/toolkit/archiver.py @@ -3,7 +3,9 @@ #from logging import raiseExceptions import logging import traceback -from clients.attribute_wrapper import attribute_wrapper + +from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper + from tango import DeviceProxy, AttributeProxy from datetime import datetime, timedelta diff --git a/devices/toolkit/archiver_base.py b/tangostationcontrol/tangostationcontrol/toolkit/archiver_base.py similarity index 100% rename from devices/toolkit/archiver_base.py rename to tangostationcontrol/tangostationcontrol/toolkit/archiver_base.py diff --git a/devices/toolkit/archiver_config/lofar2.json b/tangostationcontrol/tangostationcontrol/toolkit/archiver_config/lofar2.json similarity index 100% rename from devices/toolkit/archiver_config/lofar2.json rename to tangostationcontrol/tangostationcontrol/toolkit/archiver_config/lofar2.json diff --git a/devices/toolkit/get_internal_attribute_history.py b/tangostationcontrol/tangostationcontrol/toolkit/get_internal_attribute_history.py similarity index 99% rename from devices/toolkit/get_internal_attribute_history.py rename to tangostationcontrol/tangostationcontrol/toolkit/get_internal_attribute_history.py index 735be01613611d73f39678f6b0ed5fb1f1c56a70..8b6b4254f6c10dc8207232b16fd5e22d44e7c556 100644 --- a/devices/toolkit/get_internal_attribute_history.py +++ b/tangostationcontrol/tangostationcontrol/toolkit/get_internal_attribute_history.py @@ -1,10 +1,8 @@ #! /usr/bin/env python3 - from tango import DeviceProxy from numpy import array, transpose - def get_internal_attribute_history(device: DeviceProxy, attribute_name: str, depth: int = 10): try: history = array(device.attribute_history(attr_name = attribute_name, depth = depth)) diff --git a/devices/toolkit/lofar2_config.py b/tangostationcontrol/tangostationcontrol/toolkit/lofar2_config.py similarity index 99% rename from devices/toolkit/lofar2_config.py rename to tangostationcontrol/tangostationcontrol/toolkit/lofar2_config.py index 581eea4f73a4d276613123ec9bf86bdb7e97a0ea..811f4f27abbbac832c6de6811a223a27229e9032 100644 --- a/devices/toolkit/lofar2_config.py +++ b/tangostationcontrol/tangostationcontrol/toolkit/lofar2_config.py @@ -2,7 +2,6 @@ import logging - def configure_logging(): # Always also log the hostname because it makes the origin of the log clear. import socket diff --git a/tangostationcontrol/tangostationcontrol/toolkit/lts_cold_start.py b/tangostationcontrol/tangostationcontrol/toolkit/lts_cold_start.py new file mode 100644 index 0000000000000000000000000000000000000000..b97e6d6fe9ef5a75aa37ac398b5dda328002d6e8 --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/toolkit/lts_cold_start.py @@ -0,0 +1,222 @@ +#! /usr/bin/env python3 +import logging +from time import sleep + +from tangostationcontrol.toolkit.startup import startup +from tangostationcontrol.toolkit.lofar2_config import configure_logging + + +def start_device(device: str): + ''' + Start a Tango device with the help of the startup function. + The device will not be forced to got through + OFF/INIT/STANDBY/ON but it is assumed that the device is in OFF + state. If the device is not in OFF state, then an exception + will be raised. + ''' + dev = startup(device = device, force_restart = False) + state = device.state() + if state is not tango._tango.DevState.ON: + raise Exception("Device \"{}\" is unexpectedly in \"{}\" state but it is expected to be in \"{}\" state. Please check the reason for the unexpected device state. Aborting the start-up procedure.".format(device, state, tango._tango.DevState.ON)) + return device + + +def lts_cold_start(): + ''' + What is this? + This is the LTS (LOFAR Test - and I forgot what S stands for) cold start + procedure cast into source code. The procedure can be found there: + https://support.astron.nl/confluence/display/L2M/LTS+startup+procedure + + Paulus wrote already a script that - illegally ;) - makes direct use of the + OPC-UA servers to accomplish the same thing that we are doing here. + Paulus' script can be found there: + https://git.astron.nl/lofar2.0/pypcc/-/blob/master/scripts/Startup.py + Thanks, Paulus! You made it very easy for me to cobble together this + script. + + For obvious reasons is our script much better though. :) + First, it is bigger. And bigger is always better. + Then it is better documented but that does not count in the HW world. + But it also raises exceptions with error messages that make an attempt to + help the user reading them and shuts down the respective Tango device(s) if + something goes south. + And that is where we try to do it really right: there is no reason to be + excessively verbatim when things work like they are expected to work. But + tell the user when something goes wrong, give an indication of what could + have gone wrong and where to look for the problem. + + Again, Paulus' script contains already very good indications where problems + might lie and made my job very easy. + + No parameters, parameters are for wimps. :) + ''' + # Define the LOFAR2.0 specific log format + configure_logging() + + # Get a reference to the RECV device, do not + # force a restart of the already running Tango + # device. + recv = startup("LTS/RECV/1") + + # Getting CLK, RCU & RCU ADCs into proper shape for use by real people. + # + # The start-up needs to happen in this sequence due to HW dependencies + # that can introduce issues which are then becoming very complicated to + # handle in SW. Therefore to keep it as simple as possible, let's stick + # to the rule recommended by Paulus: + # 1 CLK + # 2 RCU + # 3 RCU ADCs + # + # + # First take the CLK board through the motions. + # 1.1 Switch off CLK + # 1.2 Wait for CLK_translator_busy_R == True, throw an exception in timeout + # 1.3 Switch on CLK + # 1.4 Wait for CLK_translator_busy_R == True, throw an exception in timeout + # 1.5 Check if CLK_PLL_locked_R == True + # 1.6 Done + # + # + # Steps 1.1 & 1.2 + recv.CLK_off() + # 2021-04-30, Thomas + # This should be refactored into a function. + timeout = 10.0 + while recv.CLK_translator_busy_R is True: + logging.debug("Waiting on \"CLK_translator_busy_R\" to become \"True\"...") + timeout = timeout - 1.0 + if timeout < 1.0: + # Switching the RECV clock off should never take longer than + # 10 seconds. Here we ran into a timeout. + # Clean up and raise an exception. + recv.off() + raise Exception("After calling \"CLK_off\" a timeout occured while waiting for \"CLK_translator_busy_R\" to become \"True\". Please investigate the reason why the RECV translator never set \"CLK_translator_busy_R\" to \"True\". Aborting start-up procedure.") + sleep(1.0) + + # Steps 1.3 & 1.4 + recv.CLK_on() + # Per Paulus this should never take longer than 2 seconds. + # 2021-04-30, Thomas + # This should be refactored into a function. + timeout = 2.0 + while recv.CLK_translator_busy_R is True: + logging.debug("After calling \"CLK_on()\" Waiting on \"CLK_translator_busy_R\" to become \"True\"...") + timeout = timeout - 1.0 + if timeout < 1.0: + # Switching theRECV clock on should never take longer than + # a couple of seconds. Here we ran into a timeout. + # Clean up and raise an exception. + recv.off() + raise Exception("After calling \"CLK_on\" a timeout occured while waiting for \"CLK_translator_busy_R\" to become \"True\". Please investigate the reason why the RECV translator never set \"CLK_translator_busy_R\" to \"True\". Aborting start-up procedure.") + sleep(1.0) + + # 1.5 Check if CLK_PLL_locked_R == True + # 2021-04-30, Thomas + # This should be refactored into a function. + clk_locked = recv.CLK_PLL_locked_R + if clk_locked is True: + logging.info("CLK signal is locked.") + else: + # CLK signal is not locked + clk_i2c_status = recv.CLK_I2C_STATUS_R + exception_text = "CLK I2C is not working. Please investigate! Maybe power cycle subrack to restart CLK board and translator. Aborting start-up procedure." + if i2c_status <= 0: + exception_text = "CLK signal is not locked. Please investigate! The subrack probably do not receive clock input or the CLK PCB is broken. Aborting start-up procedure." + recv.off() + raise Exception(exception_text) + # Step 1.6 + # Done. + + # 2 RCUs + # If we reach this point in the start-up procedure, then the CLK board setup + # is done. We can proceed with the RCUs. + # + # Now take the RCUs through the motions. + # 2.1 Set RCU mask to all available RCUs + # 2.2 Switch off all RCUs + # 2.3 Wait for RCU_translator_busy_R = True, throw an exception in timeout + # 2.4 Switch on RCUs + # 2.5 Wait for RCU_translator_busy_R = True, throw an exception in timeout + # 2.6 Done + # + # + # Step 2.1 + # We have only 8 RCUs in LTS. + recv.RCU_mask_RW = [True, ] * 8 + # Steps 2.2 & 2.3 + recv.RCU_off() + # 2021-04-30, Thomas + # This should be refactored into a function. + timeout = 10.0 + while recv.RCU_translator_busy_R is True: + logging.debug("Waiting on \"RCU_translator_busy_R\" to become \"True\"...") + timeout = timeout - 1.0 + if timeout < 1.0: + # Switching the RCUs off should never take longer than + # 10 seconds. Here we ran into a timeout. + # Clean up and raise an exception. + recv.off() + raise Exception("After calling \"RCU_off\" a timeout occured while waiting for \"RCU_translator_busy_R\" to become \"True\". Please investigate the reason why the RECV translator never set \"RCU_translator_busy_R\" to \"True\". Aborting start-up procedure.") + sleep(1.0) + + # Steps 2.4 & 2.5 + # We leave the RCU mask as it is because it got already set for the + # RCU_off() call. + recv.RCU_on() + # Per Paulus this should never take longer than 5 seconds. + # 2021-04-30, Thomas + # This should be refactored into a function. + timeout = 5.0 + while recv.RCU_translator_busy_R is True: + logging.debug("After calling \"RCU_on()\" Waiting on \"RCU_translator_busy_R\" to become \"True\"...") + timeout = timeout - 1.0 + if timeout < 1.0: + # Switching the RCUs on should never take longer than + # a couple of seconds. Here we ran into a timeout. + # Clean up and raise an exception. + recv.off() + raise Exception("After calling \"RCU_on\" a timeout occured while waiting for \"RCU_translator_busy_R\" to become \"True\". Please investigate the reason why the RECV translator never set \"RCU_translator_busy_R\" to \"True\". Aborting start-up procedure.") + sleep(1.0) + # Step 2.6 + # Done. + + # 3 ADCs + # If we get here, we only got to check if the ADCs are locked, too. + # 3.1 Check RCUs' I2C status + # 3.2 Check RCU_ADC_lock_R == [True, ] for RCUs that have a good I2C status + # 3.3 Done + # + # + # Steps 3.1 & 3.2 + rcu_mask = recv.RCU_mask_RW + adc_locked = numpy.array(recv.RCU_ADC_lock_R) + for rcu, i2c_status in enumerate(recv.RCU_I2C_STATUS_R): + if i2c_status == 0: + rcu_mask[rcu] = True + logging.info("RCU #{} is available.".format(rcu)) + for adc, adc_is_locked in enumerate(adc_locked[rcu]): + if adc_is_locked < 1: + logging.warning("RCU#{}, ADC#{} is unlocked. Please investigate! Will continue with normal operation.".format(rcu, adc)) + else: + # The RCU's I2C bus is not working. + rcu_mask[rcu] = False + logging.error("RCU #{}'s I2C is not working. Please investigate! Disabling RCU #{} to avoid damage.".format(rcu, rcu)) + recv.RCU_mask_RW = rcu_mask + # Step 3.3 + # Done + + # Start-up APSCTL, i.e. Uniboard2s. + aps = startup("APSCTL/SDP/1") + logging.warning("Cannot start-up APSCTL because it requires manual actions.") + + # Start up SDP, i.e. configure the firmware in the Unibards + sdp = startup("LTS/SDP/1") + logging.warning("Cannot start-up SDP because it requires manual actions.") + + logging.info("LTS has been successfully started and configured.") + +def main(args=None, **kwargs): + + return lts_cold_start() diff --git a/tangostationcontrol/tangostationcontrol/toolkit/startup.py b/tangostationcontrol/tangostationcontrol/toolkit/startup.py new file mode 100644 index 0000000000000000000000000000000000000000..66a8d2c496fc7e86d0d13086336e900fc1a1bfaf --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/toolkit/startup.py @@ -0,0 +1,49 @@ +#! /usr/bin/env python3 +import tango +import logging + +logger = logging.getLogger() + +def startup(device: str, force_restart: bool) -> tango.DeviceProxy: + ''' + Start a LOFAR Tango device: + recv = startup(device = 'LTS/RECV/1', force_restart = False) + ''' + proxy = tango.DeviceProxy(device) + state = proxy.state() + + # go to OFF, but only if force_restart is True + if force_restart is True: + logger.warning(f"Forcing device {device} restart.") + proxy.off() + state = proxy.state() + if state is not tango._tango.DevState.OFF: + logger.error(f"Device {device} cannot perform off although restart has been enforced, state = {state}. Please investigate.") + return proxy + + if state is not tango._tango.DevState.OFF: + logger.error(f"Device {device} is not in OFF state, cannot start it. state = {state}") + return proxy + + # Initialise device + logger.info(f"Device {device} is in OFF, performing initialisation.") + proxy.initialise() + state = proxy.state() + if state is not tango._tango.DevState.STANDBY: + logger.error(f"Device {device} cannot perform initialise, state = {state}. Please investigate.") + return proxy + + # Set default values + logger.info(f"Device {device} is in STANDBY, setting default values.") + proxy.set_defaults() + + # Turn on device + logger.info(f"Device {device} is in STANDBY, performing on.") + proxy.on() + state = proxy.state() + if state is not tango._tango.DevState.ON: + logger.error(f"Device {device} cannot perform on, state = {state}. Please investigate.") + else: + logger.info(f"Device {device} has successfully reached ON state.") + + return proxy diff --git a/devices/toolkit/udp_simulator.py b/tangostationcontrol/tangostationcontrol/toolkit/udp_simulator.py similarity index 100% rename from devices/toolkit/udp_simulator.py rename to tangostationcontrol/tangostationcontrol/toolkit/udp_simulator.py diff --git a/devices/version.py b/tangostationcontrol/tangostationcontrol/version.py similarity index 100% rename from devices/version.py rename to tangostationcontrol/tangostationcontrol/version.py diff --git a/devices/test-requirements.txt b/tangostationcontrol/test-requirements.txt similarity index 100% rename from devices/test-requirements.txt rename to tangostationcontrol/test-requirements.txt diff --git a/devices/tox.ini b/tangostationcontrol/tox.ini similarity index 95% rename from devices/tox.ini rename to tangostationcontrol/tox.ini index 59d2347f3ff42ccb084033aea67d478fd63513cb..69782d5e33c1ab14ce9abcd50253e0db9eabdf9a 100644 --- a/devices/tox.ini +++ b/tangostationcontrol/tox.ini @@ -22,7 +22,7 @@ commands = stestr run {posargs} ; Warning running integration tests will make changes to your docker system! ; These tests should only be run by the integration-test docker container. passenv = TANGO_HOST -setenv = TESTS_DIR=./integration_test +setenv = TESTS_DIR=./tangostationcontrol/integration_test commands = stestr run --serial {posargs}