diff --git a/devices/devices/docker_device.py b/devices/devices/docker_device.py index 5ff0ec366c436a2dfc75d4cd479219a04c6938d3..81abc26ee8ba4021b8e52bcefcf420a20cbdf28a 100644 --- a/devices/devices/docker_device.py +++ b/devices/devices/docker_device.py @@ -37,19 +37,6 @@ __all__ = ["Docker", "main"] @device_logging_to_python() class Docker(hardware_device): - """ - - **Properties:** - - - Device Property - OPC_Server_Name - - Type:'DevString' - OPC_Server_Port - - Type:'DevULong' - OPC_Time_Out - - Type:'DevDouble' - """ - # ----------------- # Device Properties # ----------------- @@ -103,19 +90,6 @@ class Docker(hardware_device): tango_rest_R = attribute_wrapper(comms_annotation={"container": "tango-rest"}, datatype=numpy.bool_) tango_rest_RW = attribute_wrapper(comms_annotation={"container": "tango-rest"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) - @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 # -------- @@ -124,9 +98,9 @@ class Docker(hardware_device): """ user code here. is called when the state is set to OFF """ # Stop keep-alive try: - self.opcua_connection.stop() + self.docker_client.stop() except Exception as e: - self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e)) + self.warn_stream("Exception while stopping docker client in configure_for_off function: {}. Exception ignored".format(e)) @log_exceptions() def configure_for_initialise(self): @@ -135,14 +109,9 @@ class Docker(hardware_device): # set up the Docker client self.docker_client = DockerClient(self.Docker_Base_URL, self.Fault, self) - # map an access helper class + # tie attributes to client for i in self.attr_list(): - try: - i.set_comm_client(self.docker_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 attribute {} read/write function. {}".format(i, e)) + i.set_comm_client(self.docker_client) self.docker_client.start() diff --git a/devices/devices/hardware_device.py b/devices/devices/hardware_device.py index f8f6ca50d7e02f5a8694c2ec4f9135dd874cd516..1035f8460e524ecd4db8ed1d8e3d4aba2f014691 100644 --- a/devices/devices/hardware_device.py +++ b/devices/devices/hardware_device.py @@ -66,6 +66,7 @@ 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. """ @@ -74,6 +75,19 @@ 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 # -------- @@ -180,18 +194,6 @@ 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() diff --git a/devices/devices/opcua_device.py b/devices/devices/opcua_device.py new file mode 100644 index 0000000000000000000000000000000000000000..d95a8426ed0dc260c0f6eb6d85149e3f5f0ec4ba --- /dev/null +++ b/devices/devices/opcua_device.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# +# This file represents a top-level device +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" Generic OPC-UA Device Server for LOFAR2.0 + +""" + +# TODO(Corne): Remove sys.path.append hack once packaging is in place! +import os, sys +currentdir = os.path.dirname(os.path.realpath(__file__)) +parentdir = os.path.dirname(currentdir) +sys.path.append(parentdir) + +# PyTango imports +from tango import DebugIt +from tango.server import device_property, attribute +from tango import AttrWriteType +import numpy +# Additional import + +from devices.device_decorators import * + +from clients.opcua_client import OPCUAConnection +from devices.hardware_device import hardware_device +from common.lofar_logging import device_logging_to_python, log_exceptions +from common.lofar_git import get_version + +__all__ = ["opcua_device", "main"] + +class opcua_device(hardware_device): + """ + + **Properties:** + + - Device Property + OPC_Server_Name + - Type:'DevString' + OPC_Server_Port + - Type:'DevULong' + OPC_Time_Out + - Type:'DevDouble' + """ + + # ----------------- + # 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 + ) + + OPC_namespace = device_property( + dtype='DevString', + mandatory=False, + default_value="http://lofar.eu" + ) + + # ---------- + # Attributes + # ---------- + + opcua_missing_attributes_R = attribute(max_dim_x=128, dtype=(str,), fget=lambda self: numpy.array(self.opcua_missing_attributes, dtype=str), doc="OPC-UA attributes that this device requested, but which are not exposed on the server. These attributes are replaced with a no-op and thus do not function as expected.") + + # -------- + # overloaded functions + # -------- + + @log_exceptions() + def configure_for_initialise(self): + """ user code here. is called when the state is set to INIT """ + + # set up the OPC ua client + self.opcua_connection = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), self.OPC_namespace, self.OPC_Time_Out, self.Fault, self) + self.opcua_missing_attributes = [] + + # map an access helper class + for i in self.attr_list(): + try: + if not i.comms_id or i.comms_id == OPCUAConnection: + i.set_comm_client(self.opcua_connection) + except Exception as e: + # use the pass function instead of setting read/write fails + i.set_pass_func() + self.opcua_missing_attributes.append(",".join(i.comms_annotation)) + + self.warn_stream("error while setting the attribute {} read/write function. {}".format(i, e)) + + self.opcua_connection.start() + + @log_exceptions() + def configure_for_off(self): + """ user code here. is called when the state is set to OFF """ + try: + # disconnect + 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)) + diff --git a/devices/devices/recv.py b/devices/devices/recv.py index 6f1de6aedc9e6db463c2edcd7a1a8bdf3daf7c2e..ddf834e14bfa9b8a6206ccc072ad4fee204c56ab 100644 --- a/devices/devices/recv.py +++ b/devices/devices/recv.py @@ -27,52 +27,19 @@ import numpy from device_decorators import * -from clients.opcua_client import OPCUAConnection from clients.attribute_wrapper import attribute_wrapper -from devices.hardware_device import hardware_device +from devices.opcua_device import opcua_device from common.lofar_logging import device_logging_to_python, log_exceptions from common.lofar_git import get_version __all__ = ["RECV", "main"] @device_logging_to_python() -class RECV(hardware_device): - """ - - **Properties:** - - - Device Property - OPC_Server_Name - - Type:'DevString' - OPC_Server_Port - - Type:'DevULong' - OPC_Time_Out - - Type:'DevDouble' - """ - +class RECV(opcua_device): # ----------------- # 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 - ) - OPC_namespace = device_property( - dtype='DevString', - mandatory=False - ) - # ---------- # Attributes # ---------- @@ -110,31 +77,9 @@ class RECV(hardware_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 """ @@ -146,20 +91,7 @@ class RECV(hardware_device): 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() - + super().configure_for_initialise() # -------- diff --git a/devices/devices/sdp/sdp.py b/devices/devices/sdp/sdp.py index 16f6d89737742f1f3d475c619a60513e4579a115..e39663c34767cc6fcbe74e6c2885b4825257a934 100644 --- a/devices/devices/sdp/sdp.py +++ b/devices/devices/sdp/sdp.py @@ -24,9 +24,8 @@ from tango.server import device_property, attribute from tango import AttrWriteType # Additional import -from clients.opcua_client import OPCUAConnection from clients.attribute_wrapper import attribute_wrapper -from devices.hardware_device import hardware_device +from devices.opcua_device import opcua_device from common.lofar_logging import device_logging_to_python, log_exceptions from common.lofar_git import get_version @@ -36,39 +35,11 @@ import numpy __all__ = ["SDP", "main"] @device_logging_to_python() -class SDP(hardware_device): - """ - - **Properties:** - - - Device Property - OPC_Server_Name - - Type:'DevString' - OPC_Server_Port - - Type:'DevULong' - OPC_Time_Out - - Type:'DevDouble' - """ - +class SDP(opcua_device): # ----------------- # 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 - ) - FPGA_processing_enable_RW_default = device_property( dtype='DevVarBooleanArray', mandatory=False, @@ -151,55 +122,9 @@ class SDP(hardware_device): TR_tod_R = attribute_wrapper(comms_annotation=["2:TR_tod_R"], datatype=numpy.int64, dims=(2,)) TR_tod_pps_delta_R = attribute_wrapper(comms_annotation=["2:TR_tod_pps_delta_R"], datatype=numpy.double) - def always_executed_hook(self): - """Method always executed before any TANGO command is executed.""" - pass - - @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_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 diff --git a/devices/devices/sdp/statistics.py b/devices/devices/sdp/statistics.py index 7d0b970b089ff29931bfc088f8b4b208d347402c..a884783ddd85c669e35a2230e72e3e4ca2f85d60 100644 --- a/devices/devices/sdp/statistics.py +++ b/devices/devices/sdp/statistics.py @@ -29,7 +29,7 @@ from clients.statistics_client import StatisticsClient from clients.opcua_client import OPCUAConnection from clients.attribute_wrapper import attribute_wrapper -from devices.hardware_device import hardware_device +from devices.opcua_device import opcua_device from common.lofar_git import get_version from common.lofar_logging import device_logging_to_python, log_exceptions @@ -41,7 +41,7 @@ import numpy __all__ = ["Statistics"] -class Statistics(hardware_device, metaclass=ABCMeta): +class Statistics(opcua_device, metaclass=ABCMeta): # In derived classes, set this to a subclass of StatisticsCollector @property @@ -53,21 +53,6 @@ class Statistics(hardware_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 @@ -124,16 +109,15 @@ class Statistics(hardware_device, metaclass=ABCMeta): except Exception as e: logger.exception("Exception while stopping statistics_client in configure_for_off. Exception ignored") - try: - self.OPCUA_client.stop() - except Exception as e: - logger.exception("Exception while stopping OPC UA connection in configure_for_off. Exception ignored") + super().configure_for_off() @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, @@ -148,27 +132,12 @@ class Statistics(hardware_device, metaclass=ABCMeta): self.statistics_collector = self.STATISTICS_COLLECTOR_CLASS() self.statistics_client = StatisticsClient(self.statistics_collector, udp_options, tcp_options, self.Fault, self) - - 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: - if i.comms_id == StatisticsClient: - i.set_comm_client(self.statistics_client) - elif i.comms_id == OPCUAConnection: - i.set_comm_client(self.OPCUA_client) - else: - raise ValueError("Cannot set comm client for attribute {}: Unknown comms_id {}".format(i, i.comms_id)) - 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 sst attribute {} read/write function. {}. using pass function instead".format(i, e)) - pass - self.statistics_client.start() - self.OPCUA_client.start() + # tie attributes to client + for i in self.attr_list(): + if i.comms_id == StatisticsClient: + i.set_comm_client(self.statistics_client) # -------- # Commands diff --git a/devices/devices/unb2.py b/devices/devices/unb2.py index 7c2575991605354de5bba608906fb9ea248f021b..83fb44ca93cb38795cc77b49354aa53dfafc5cf8 100644 --- a/devices/devices/unb2.py +++ b/devices/devices/unb2.py @@ -23,9 +23,8 @@ from tango.server import device_property, attribute from tango import AttrWriteType # Additional import -from clients.opcua_client import OPCUAConnection from clients.attribute_wrapper import attribute_wrapper -from devices.hardware_device import hardware_device +from devices.opcua_device import opcua_device from common.lofar_logging import device_logging_to_python, log_exceptions from common.lofar_git import get_version @@ -35,39 +34,11 @@ import numpy __all__ = ["UNB2", "main"] @device_logging_to_python() -class UNB2(hardware_device): - """ - - **Properties:** - - - Device Property - OPC_Server_Name - - Type:'DevString' - OPC_Server_Port - - Type:'DevULong' - OPC_Time_Out - - Type:'DevDouble' - """ - +class UNB2(opcua_device): # ----------------- # 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 - ) - # ---------- # Attributes # ---------- @@ -167,49 +138,10 @@ class UNB2(hardware_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