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

fixed 'illegal state transition' bug and off->initialise state bug

parent dde30236
No related branches found
No related tags found
2 merge requests!18Resolve #2021 "04 16 branched from master state bug fix",!17Resolve #2021 "04 16 branched from master state bug fix"
......@@ -17,24 +17,24 @@ from tango.server import run
from src.hardware_device import *
__all__ = ["HW_dev"]
class HW_dev(hardware_device):
"""
This class is the minimal (read empty) implementation of a class using 'hardware_device'
"""
This class is the minimal (read empty) implementation of a class using 'hardware_device'
"""
# ----------
# Attributes
# ----------
"""
attribute wrapper objects can be declared here. All attribute wrapper objects will get automatically put in a ist (attr_list) for easy access
attribute wrapper objects can be declared here. All attribute wrapper objects will get automatically put in a ist (attr_list) for easy access
example = attribute_wrapper(comms_annotation="this is an example", datatype=numpy.double, dims=(8, 2), access=AttrWriteType.READ_WRITE)
...
example = attribute_wrapper(comms_annotation="this is an example", datatype=numpy.double, dims=(8, 2), access=AttrWriteType.READ_WRITE)
...
"""
"""
def always_executed_hook(self):
"""Method always executed before any TANGO command is executed."""
......@@ -43,10 +43,10 @@ class HW_dev(hardware_device):
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).
"""
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()
......@@ -76,6 +76,7 @@ class HW_dev(hardware_device):
""" user code here. is called when the sate is set to INIT """
pass
# ----------
# Run server
# ----------
......@@ -86,4 +87,3 @@ def main(args=None, **kwargs):
if __name__ == '__main__':
main()
This diff is collapsed.
......@@ -15,21 +15,33 @@
from tango import DebugIt
from tango.server import run, command
from tango.server import device_property
# Additional import
from clients.opcua_connection import OPCUAConnection
from src.attribute_wrapper import *
from src.hardware_device import *
from src.lofar_logging import device_logging_to_python
__all__ = ["PCC", "main"]
@device_logging_to_python({"device": "PCC"})
class PCC(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
......@@ -44,8 +56,7 @@ class PCC(hardware_device):
dtype='DevDouble',
mandatory=True
)
OPC_Namespace = device_property(
OPC_namespace = device_property(
dtype='DevString',
mandatory=False
)
......@@ -53,65 +64,46 @@ class PCC(hardware_device):
# ----------
# Attributes
# ----------
RCU_state_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_state_R"], datatype=numpy.str_, access=AttrWriteType.READ_WRITE)
RCU_mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_mask_RW"], datatype=numpy.bool_, dims=(32,), access=AttrWriteType.READ_WRITE)
Ant_mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:Ant_mask_RW"], datatype=numpy.bool_, dims=(3, 32), access=AttrWriteType.READ_WRITE)
RCU_attenuator_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_attenuator_R"], datatype=numpy.int64, dims=(3, 32))
RCU_attenuator_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_attenuator_RW"], datatype=numpy.int64, dims=(3, 32), access=AttrWriteType.READ_WRITE)
RCU_attenuator_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_attenuator_RW"], datatype=numpy.int64, dims=(3, 32),
access=AttrWriteType.READ_WRITE)
RCU_band_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_band_R"], datatype=numpy.int64, dims=(3, 32))
RCU_band_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_band_RW"], datatype=numpy.int64, dims=(3, 32), access=AttrWriteType.READ_WRITE)
RCU_temperature_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_temperature_R"], datatype=numpy.float64, dims=(32,))
RCU_Pwr_dig_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_Pwr_dig_R"], datatype=numpy.int64, dims=(32,))
RCU_LED0_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_LED0_R"], datatype=numpy.int64, dims=(32,))
RCU_LED0_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_LED0_RW"], datatype=numpy.int64, dims=(32,), access=AttrWriteType.READ_WRITE)
RCU_ADC_lock_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_ADC_lock_R"], datatype=numpy.int64, dims=(3, 32))
RCU_ADC_SYNC_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_ADC_SYNC_R"], datatype=numpy.int64, dims=(3, 32))
RCU_ADC_JESD_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_ADC_JESD_R"], datatype=numpy.int64, dims=(3, 32))
RCU_ADC_CML_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_ADC_CML_R"], datatype=numpy.int64, dims=(3, 32))
RCU_OUT1_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_OUT1_R"], datatype=numpy.int64, dims=(3, 32))
RCU_OUT2_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_OUT2_R"], datatype=numpy.int64, dims=(3, 32))
RCU_ID_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_ID_R"], datatype=numpy.int64, dims=(32,))
RCU_version_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_version_R"], datatype=numpy.str_, dims=(32,))
HBA_element_beamformer_delays_R = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_beamformer_delays_R"], datatype=numpy.int64, dims=(32, 96))
HBA_element_beamformer_delays_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_beamformer_delays_RW"], datatype=numpy.int64, dims=(32, 96), access=AttrWriteType.READ_WRITE)
HBA_element_beamformer_delays_R = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_beamformer_delays_R"], datatype=numpy.int64,
dims=(32, 96))
HBA_element_beamformer_delays_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_beamformer_delays_RW"], datatype=numpy.int64,
dims=(32, 96), access=AttrWriteType.READ_WRITE)
HBA_element_pwr_R = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_pwr_R"], datatype=numpy.int64, dims=(32, 96))
HBA_element_pwr_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_pwr_RW"], datatype=numpy.int64, dims=(32, 96),
access=AttrWriteType.READ_WRITE)
HBA_element_pwr_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_pwr_RW"], datatype=numpy.int64, dims=(32, 96), access=AttrWriteType.READ_WRITE)
uC_ID_R = attribute_wrapper(comms_annotation=["2:PCC", "2:uC_ID_R"], datatype=numpy.int64, dims=(32,))
RCU_monitor_rate_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_monitor_rate_RW"], datatype=numpy.float64, access=AttrWriteType.READ_WRITE)
RCU_monitor_rate_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_monitor_rate_RW"], datatype=numpy.float64,
access=AttrWriteType.READ_WRITE)
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).
"""
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.")
......@@ -122,17 +114,24 @@ class PCC(hardware_device):
""" user code here. is called when the state is set to OFF """
# Stop keep-alive
self.OPCua_client.disconnect()
self.OPCua_client.stop()
def initialise(self):
""" user code here. is called when the state is set to INIT """
#set up the OPC ua client
namespace = "http://lofar.eu"
if self.OPC_Namespace is not None:
namespace = self.OPC_Namespace
self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), namespace, self.OPC_Time_Out, self.Standby, self.Fault, self)
# 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["ADC_on"] = {}
self.function_mapping["RCU_update"] = {}
self.function_mapping["CLK_on"] = {}
self.function_mapping["CLK_off"] = {}
self.function_mapping["CLK_PLL_setup"] = {}
# 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 the attributes to the OPC ua comm client
for i in self.attr_list():
......@@ -141,16 +140,6 @@ class PCC(hardware_device):
except:
pass
# Init the dict that contains function to OPC-UA function mappings.
self.function_mapping = {}
self.function_mapping["RCU_off"] = self.OPCua_client._setup_annotation(["2:PCC", "2:RCU_off"])
self.function_mapping["RCU_on"] = self.OPCua_client._setup_annotation(["2:PCC", "2:RCU_on"])
self.function_mapping["ADC_on"] = self.OPCua_client._setup_annotation(["2:PCC", "2:ADC_on"])
self.function_mapping["RCU_update"] = self.OPCua_client._setup_annotation(["2:PCC", "2:RCU_update"])
self.function_mapping["CLK_off"] = self.OPCua_client._setup_annotation(["2:PCC", "2:CLK_off"])
self.function_mapping["CLK_on"] = self.OPCua_client._setup_annotation(["2:PCC", "2:CLK_on"])
self.function_mapping["CLK_PLL_setup"] = self.OPCua_client._setup_annotation(["2:PCC", "2:CLK_PLL_setup"])
self.OPCua_client.start()
# --------
......@@ -163,8 +152,8 @@ class PCC(hardware_device):
def RCU_off(self):
"""
:return:None
"""
:return:None
"""
self.function_mapping["RCU_off"]()
@command()
......@@ -174,8 +163,8 @@ class PCC(hardware_device):
def RCU_on(self):
"""
:return:None
"""
:return:None
"""
self.function_mapping["RCU_on"]()
@command()
......@@ -185,8 +174,8 @@ class PCC(hardware_device):
def ADC_on(self):
"""
:return:None
"""
:return:None
"""
self.function_mapping["ADC_on"]()
@command()
......@@ -196,8 +185,8 @@ class PCC(hardware_device):
def RCU_update(self):
"""
:return:None
"""
:return:None
"""
self.function_mapping["RCU_update"]()
@command()
......@@ -207,8 +196,8 @@ class PCC(hardware_device):
def CLK_off(self):
"""
:return:None
"""
:return:None
"""
self.function_mapping["CLK_off"]()
@command()
......@@ -218,8 +207,8 @@ class PCC(hardware_device):
def CLK_on(self):
"""
:return:None
"""
:return:None
"""
self.function_mapping["CLK_on"]()
@command()
......@@ -229,8 +218,8 @@ class PCC(hardware_device):
def CLK_PLL_setup(self):
"""
:return:None
"""
:return:None
"""
self.function_mapping["CLK_PLL_setup"]()
......@@ -244,4 +233,3 @@ def main(args=None, **kwargs):
if __name__ == '__main__':
main()
......@@ -19,12 +19,10 @@ from tango.server import device_property
from clients.opcua_connection import OPCUAConnection
from src.attribute_wrapper import *
from src.hardware_device import *
from src.lofar_logging import device_logging_to_python
__all__ = ["SDP", "main"]
@device_logging_to_python({"device": "SDP"})
class SDP(hardware_device):
"""
......@@ -105,10 +103,7 @@ class SDP(hardware_device):
"""Initialises the attributes and properties of the PCC."""
# 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.Standby, self.Fault, self)
# will contain all the values for this object
self.setup_value_dict()
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():
......
from src.comms_client import *
import opcua
__all__ = ["OPCUAConnection"]
......@@ -33,13 +33,13 @@ class OPCUAConnection(CommClient):
def start(self):
super().start()
def __init__(self, address, namespace, timeout, on_func, fault_func, streams, try_interval=2):
def __init__(self, address, namespace, timeout, fault_func, streams, try_interval=2):
"""
Create the OPC ua client and connect() to it and get the object node
"""
super().__init__(on_func, fault_func, streams, try_interval)
super().__init__(fault_func, streams, try_interval)
self.client = opcua.Client(address, timeout)
self.client = Client(address, timeout)
# Explicitly connect
if not self.connect():
......@@ -77,7 +77,7 @@ class OPCUAConnection(CommClient):
return True
except socket.error as e:
self.streams.error_stream("Could not connect to server %s: %s", self._servername(), e)
raise Exception("") from e
raise Exception("Could not connect to server %s", self._servername()) from e
def disconnect(self):
......@@ -89,19 +89,16 @@ class OPCUAConnection(CommClient):
try:
self.client.disconnect()
except Exception as e:
self.streams.error_stream("Disconnect from OPC-UA server {} failed: {}".format(self._servername(), e))
self.streams.error_stream("Disconnect from OPC-UA server %s failed: %s", self._servername(), e)
def ping(self):
"""
ping the client to make sure the connection with the client is still functional.
"""
try:
if self.connected is True:
self.client.send_hello()
else:
self.streams.debug_stream("Will not ping OPC-UA server {} because the connection is inactive.".format(self._servername()))
self.client.send_hello()
except Exception as e:
raise Exception("Lost connection to server {}.".format(self._servername())) from e
raise Exception("Lost connection to server %s: %s", self._servername(), e)
def _setup_annotation(self, annotation):
"""
......@@ -117,12 +114,13 @@ class OPCUAConnection(CommClient):
elif isinstance(annotation, list):
path = annotation
else:
raise Exception("OPC-ua mapping requires either a list of the path or dict with the path. Was given {} type containing: {}".format(type(annotation), annotation))
raise Exception("OPC-ua mapping requires either a list of the path or dict with the path. Was given %s type containing: %s", type(annotation), annotation)
try:
node = self.obj.get_child(path)
except Exception as e:
raise Exception("Could not get node: {} on server {}".format(path, self._servername())) from e
self.streams.error_stream("Could not get node: %s on server %s: %s", path, self._servername(), e)
raise Exception("Could not get node: %s on server %s", path, self._servername()) from e
return node
......@@ -134,7 +132,7 @@ class OPCUAConnection(CommClient):
dim_x = attribute.dim_x
dim_y = attribute.dim_y
ua_type = numpy_to_OPCua_dict[attribute.numpy_type] # convert the numpy type to a corresponding UA type
ua_type = numpy_to_OPCua_dict[attribute.numpy_type] # convert the numpy type to a corresponding UA type
return dim_x, dim_y, ua_type
......
from src.comms_client import *
# <class 'numpy.bool_'>
class example_client(CommClient):
"""
this class provides an example implementation of a comms_client.
Durirng initialisation it creates a correctly shaped zero filled value. on read that value is returned and on write its modified.
"""
this class provides an example implementation of a comms_client.
Durirng initialisation it creates a correctly shaped zero filled value. on read that value is returned and on write its modified.
"""
def start(self):
super().start()
def __init__(self, standby_func, fault_func, streams, try_interval=2):
"""
initialises the class and tries to connect to the client.
def __init__(self, fault_func, streams, try_interval=2):
"""
super().__init__(standby_func, fault_func, streams, try_interval)
initialises the class and tries to connect to the client.
"""
super().__init__(fault_func, streams, try_interval)
# Explicitly connect
if not self.connect():
......@@ -25,13 +26,13 @@ class example_client(CommClient):
def connect(self):
"""
this function provides a location for the code neccecary to connect to the client
"""
this function provides a location for the code neccecary to connect to the client
"""
self.streams.debug_stream("the example client doesn't actually connect to anything silly")
self.connected = True # set connected to true
return True # if succesfull, return true. otherwise return false
self.connected = True # set connected to true
return True # if succesfull, return true. otherwise return false
def disconnect(self):
self.connected = False # always force a reconnect, regardless of a successful disconnect
......@@ -39,23 +40,23 @@ class example_client(CommClient):
def _setup_annotation(self, annotation):
"""
this function gives the client access to the comm client annotation data given to the attribute wrapper.
The annotation data can be used to provide whatever extra data is necessary in order to find/access the monitor/control point.
this function gives the client access to the comm client annotation data given to the attribute wrapper.
The annotation data can be used to provide whatever extra data is necessary in order to find/access the monitor/control point.
the annotation can be in whatever format may be required. it is up to the user to handle its content
example annotation may include:
- a file path and file line/location
- COM object path
"""
the annotation can be in whatever format may be required. it is up to the user to handle its content
example annotation may include:
- a file path and file line/location
- COM object path
"""
# as this is an example, just print the annotation
self.streams.debug_stream("annotation: {}".format(annotation))
def _setup_value_conversion(self, attribute):
"""
gives the client access to the attribute_wrapper object in order to access all
necessary data such as dimensionality and data type
"""
gives the client access to the attribute_wrapper object in order to access all
necessary data such as dimensionality and data type
"""
if attribute.dim_y > 1:
dims = (attribute.dim_y, attribute.dim_x)
......@@ -66,11 +67,10 @@ class example_client(CommClient):
return dims, dtype
def _setup_mapping(self, dims, dtype):
"""
takes all gathered data to configure and return the correct read and write functions
"""
takes all gathered data to configure and return the correct read and write functions
"""
value = numpy.zeros(dims, dtype)
......@@ -85,12 +85,11 @@ class example_client(CommClient):
self.streams.debug_stream("created and bound example_client read/write functions to attribute_wrapper object")
return read_function, write_function
def setup_attribute(self, annotation=None, attribute=None):
"""
MANDATORY function: is used by the attribute wrapper to get read/write functions.
must return the read and write functions
"""
MANDATORY function: is used by the attribute wrapper to get read/write functions.
must return the read and write functions
"""
# process the comms_annotation
self._setup_annotation(annotation)
......@@ -103,4 +102,3 @@ class example_client(CommClient):
# return the read/write functions
return read_function, write_function
#! /usr/bin/env python3
import logging
def configure_logging():
# Always also log the hostname because it makes the origin of the log clear.
import socket
hostname = socket.gethostname()
# 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.
logging.basicConfig(format = '%(asctime)s.%(msecs)d %(levelname)s - HOST="{}" PID="%(process)d" TNAME="%(threadName)s" TID="%(thread)d" FILE="%(pathname)s" LINE="%(lineno)d" FUNC="%(funcName)s" MSG="%(message)s"'.format(hostname), datefmt = '%Y-%m-%dT%H:%M:%S', level = logging.INFO, Force = True)
......@@ -5,28 +5,27 @@ import numpy
from src.wrappers import only_when_on, fault_on_error
import logging
logger = logging.getLogger()
class attribute_wrapper(attribute):
"""
Wraps all the attributes in a wrapper class to manage most of the redundant code behind the scenes
"""
Wraps all the attributes in a wrapper class to manage most of the redundant code behind the scenes
"""
def __init__(self, comms_annotation=None, datatype=None, dims=(1,), access=AttrWriteType.READ, init_value=None, **kwargs):
"""
wraps around the tango Attribute class. Provides an easier interface for 1d or 2d arrays. Also provides a way to abstract
managing the communications interface.
"""
wraps around the tango Attribute class. Provides an easier interface for 1d or 2d arrays. Also provides a way to abstract
managing the communications interface.
"""
# ensure the type is a numpy array
if "numpy" not in str(datatype) and type(datatype) != str:
raise TypeError("Attribute needs to be a Tango-supported numpy or str type, but has type \"%s\"" % (datatype,))
self.comms_annotation = comms_annotation # store data that can be used by the comms interface. not used by the wrapper itself
self.numpy_type = datatype # tango changes our attribute to their representation (E.g numpy.int64 becomes "DevLong64")
self.comms_annotation = comms_annotation # store data that can be used by the comms interface. not used by the wrapper itself
self.numpy_type = datatype # tango changes our attribute to their representation (E.g numpy.int64 becomes "DevLong64")
self.init_value = init_value
max_dim_y = 0
......@@ -54,7 +53,6 @@ class attribute_wrapper(attribute):
# scalar, just set the single dimension
max_dim_x = 1
if access == AttrWriteType.READ_WRITE:
""" if the attribute is of READ_WRITE type, assign the RW and write function to it"""
......@@ -63,22 +61,25 @@ class attribute_wrapper(attribute):
def read_RW(device):
# print("read_RW {}, {}x{}, {}, {}".format(me.name, me.dim_x, me.dim_y, me.attr_type, me.value))
"""
read_RW returns the value that was last written to the attribute
"""
read_RW returns the value that was last written to the attribute
"""
try:
return device.value_dict[self]
except Exception as e:
raise Exception("Attribute read_RW function error, attempted to read value_dict with key: `%s`, are you sure this exists?", self) from e
raise Exception("Attribute read_RW function error, attempted to read value_dict with key: `%s`, are you sure this exists?",
self) from e
@only_when_on
@fault_on_error
def write_RW(device, value):
"""
_write_RW writes a value to this attribute
"""
self.write_function(value)
device.value_dict[self] = value
_write_RW writes a value to this attribute
"""
try:
self.write_function(value)
device.value_dict[self] = value
except Exception as e:
raise e
self.fget = read_RW
self.fset = write_RW
......@@ -91,8 +92,8 @@ class attribute_wrapper(attribute):
@fault_on_error
def read_R(device):
"""
_read_R reads the attribute value, stores it and returns it"
"""
_read_R reads the attribute value, stores it and returns it"
"""
device.value_dict[self] = self.read_function()
return device.value_dict[self]
......@@ -104,8 +105,8 @@ class attribute_wrapper(attribute):
def initial_value(self):
"""
returns a numpy array filled with zeroes fit to the size of the attribute. Or if init_value is not the default None, return that value
"""
returns a numpy array filled with zeroes fit to the size of the attribute. Or if init_value is not the default None, return that value
"""
if self.init_value is not None:
return self.init_value
......@@ -125,14 +126,15 @@ class attribute_wrapper(attribute):
def set_comm_client(self, client):
"""
takes a communications client as input arguments This client should be of a class containing a "get_mapping" function
and return a read and write function that the wrapper will use to get/set data.
"""
takes a communications client as input arguments This client should be of a class containing a "get_mapping" function
and return a read and write function that the wrapper will use to get/set data.
"""
try:
self.read_function, self.write_function = client.setup_attribute(self.comms_annotation, self)
except Exception as e:
def pass_func(value=None):
pass
logger.error("setting comm_client failed. using pass function instead")
self.read_function = pass_func
......
......@@ -3,6 +3,9 @@ import socket
import time
import numpy
import opcua
from opcua import Client
from tango import DevState
......@@ -11,11 +14,10 @@ class CommClient(Thread):
The ProtocolHandler class is the generic interface class between the tango attribute_wrapper and the outside world
"""
def __init__(self, standby_func, fault_func, streams, try_interval=2):
def __init__(self, fault_func, streams, try_interval=2):
"""
"""
self.standby_func = standby_func
self.fault_func = fault_func
self.try_interval = try_interval
self.streams = streams
......@@ -45,14 +47,13 @@ class CommClient(Thread):
self.fault_func()
return
self.standby_func()
self.stopping = False
while not self.stopping:
# keep trying to connect
if not self.connected:
if self.connect():
self.standby_func()
# self.standby_func()
pass
else:
# we retry only once, to catch exotic network issues. if the infra or hardware is down,
# our device cannot help, and must be reinitialised after the infra or hardware is fixed.
......
......@@ -17,7 +17,6 @@ from tango import DevState, DebugIt
# Additional import
from src.attribute_wrapper import *
from src.lofar_logging import log_exceptions
__all__ = ["hardware_device"]
......@@ -60,7 +59,6 @@ class hardware_device(Device):
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,21 +82,22 @@ class hardware_device(Device):
"""
self.set_state(DevState.INIT)
self.setup_value_dict()
self.initialise()
self.standby()
self.set_state(DevState.STANDBY)
@only_in_states([DevState.INIT])
def Standby(self):
"""
Command to ask for initialisation of this device. Can only be called in FAULT or OFF state.
:return:None
"""
self.initialise()
self.standby()
self.set_state(DevState.STANDBY)
# @only_in_states([DevState.INIT])
# def Standby(self):
# """
# Command to ask for initialisation of this device. Can only be called in FAULT or OFF state.
#
# :return:None
# """
#
# self.standby()
# self.set_state(DevState.STANDBY)
@command()
@only_in_states([DevState.STANDBY])
@DebugIt()
......@@ -164,7 +163,6 @@ class hardware_device(Device):
"""Method always executed before any TANGO command is executed."""
pass
@log_exceptions()
def delete_device(self):
"""Hook to delete resources allocated in init_device.
......
#! /usr/bin/env python3
def startup(device: str, force_restart: bool):
'''
Start a LOFAR Tango device:
pcc = startup(device = 'LTS/PCC/1', force_restart = False)
'''
import tango
proxy = tango.DeviceProxy(device)
state = proxy.state()
if force_restart is True:
print("Forcing device {} restart.".format(device))
proxy.off()
state = proxy.state()
if state is not tango._tango.DevState.OFF:
print("Device {} cannot perform off although restart has been enforced, state = {}. Please investigate.".format(device, state))
return proxy
if state is not tango._tango.DevState.OFF:
print("Device {} is not in OFF state, cannot start it. state = {}".format(device, state))
return proxy
print("Device {} is in OFF, performing initialisation.".format(device))
proxy.initialise()
state = proxy.state()
if state is not tango._tango.DevState.STANDBY:
print("Device {} cannot perform initialise, state = {}. Please investigate.".format(device, state))
return proxy
print("Device {} is in STANDBY, performing on.".format(device))
proxy.on()
state = proxy.state()
if state is not tango._tango.DevState.ON:
print("Device {} cannot perform on, state = {}. Please investigate.".format(device, state))
else:
print("Device {} has successfully reached ON state.".format(device))
return proxy
......@@ -25,7 +25,6 @@ __all__ = ["test_device", "main"]
class test_device(hardware_device):
# -----------------
# Device Properties
# -----------------
......@@ -48,16 +47,20 @@ class test_device(hardware_device):
bool_scalar_R = attribute_wrapper(comms_annotation="numpy.bool_ type read scalar", datatype=numpy.bool_)
bool_scalar_RW = attribute_wrapper(comms_annotation="numpy.bool_ type read/write scalar", datatype=numpy.bool_, access=AttrWriteType.READ_WRITE)
int64_spectrum_R = attribute_wrapper(comms_annotation="numpy.int64 type read spectrum (len = 8)", datatype=numpy.int64, dims=(8,))
str_spectrum_RW = attribute_wrapper(comms_annotation="numpy.str type read/write spectrum (len = 8)", datatype=numpy.str_, dims=(8,), access=AttrWriteType.READ_WRITE)
int32_spectrum_R = attribute_wrapper(comms_annotation="numpy.int32 type read spectrum (len = 8)", datatype=numpy.int32, dims=(8,))
int32_spectrum_RW = attribute_wrapper(comms_annotation="numpy.int32 type read spectrum (len = 8)", datatype=numpy.int32, dims=(8,),
access=AttrWriteType.READ_WRITE)
double_image_R = attribute_wrapper(comms_annotation="numpy.double type read image (dims = 2x8)", datatype=numpy.double, dims=(2, 8))
double_image_RW = attribute_wrapper(comms_annotation="numpy.double type read/write image (dims = 8x2)", datatype=numpy.double, dims=(8, 2), access=AttrWriteType.READ_WRITE)
double_image_RW = attribute_wrapper(comms_annotation="numpy.double type read/write image (dims = 8x2)", datatype=numpy.double, dims=(8, 2),
access=AttrWriteType.READ_WRITE)
int32_scalar_R = attribute_wrapper(comms_annotation="numpy.int32 type read scalar", datatype=numpy.int32)
uint16_spectrum_RW = attribute_wrapper(comms_annotation="numpy.uint16 type read/write spectrum (len = 8)", datatype=numpy.uint16, dims=(8,), access=AttrWriteType.READ_WRITE)
uint16_spectrum_RW = attribute_wrapper(comms_annotation="numpy.uint16 type read/write spectrum (len = 8)", datatype=numpy.uint16, dims=(8,),
access=AttrWriteType.READ_WRITE)
float32_image_R = attribute_wrapper(comms_annotation="numpy.float32 type read image (dims = 8x2)", datatype=numpy.float32, dims=(8, 2))
uint8_image_RW = attribute_wrapper(comms_annotation="numpy.uint8 type read/write image (dims = 2x8)", datatype=numpy.uint8, dims=(2, 8), access=AttrWriteType.READ_WRITE)
uint8_image_RW = attribute_wrapper(comms_annotation="numpy.uint8 type read/write image (dims = 2x8)", datatype=numpy.uint8, dims=(2, 8),
access=AttrWriteType.READ_WRITE)
# --------
# overloaded functions
......@@ -68,17 +71,16 @@ class test_device(hardware_device):
self.set_state(DevState.INIT)
#set up the OPC ua client
self.example_client = example_client(self.Standby, self.Fault, self)
# set up the OPC ua client
self.example_client = example_client(self.Fault, self)
# map an access helper class
for i in self.attr_list():
i.set_comm_client(self.example_client)
self.example_client.start()
# ----------
# Run server
# ----------
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment