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

Implemented changes for merge

parent ac01b353
No related branches found
No related tags found
1 merge request!52021 03 22 branched from master attribute wrapper
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
# Distributed under the terms of the APACHE license. # Distributed under the terms of the APACHE license.
# See LICENSE.txt for more info. # See LICENSE.txt for more info.
""" PCC Device Server for LOFAR2.0 """ Hardware Device Server for LOFAR2.0
""" """
...@@ -80,7 +80,7 @@ class HW_dev(hardware_device): ...@@ -80,7 +80,7 @@ class HW_dev(hardware_device):
# Run server # Run server
# ---------- # ----------
def main(args=None, **kwargs): def main(args=None, **kwargs):
"""Main function of the PCC module.""" """Main function of the hardware device module."""
return run((HW_dev,), args=args, **kwargs) return run((HW_dev,), args=args, **kwargs)
......
...@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work. ...@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright [yyyy] [name of copyright owner] Copyright 2021 ASTRON Netherlands Institute for Radio Astronomy
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
......
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
""" """
# PyTango imports # PyTango imports
from tango.server import run from tango import DebugIt
from tango.server import run, command
from tango.server import device_property from tango.server import device_property
# Additional import # Additional import
...@@ -55,6 +56,10 @@ class PCC(hardware_device): ...@@ -55,6 +56,10 @@ class PCC(hardware_device):
dtype='DevDouble', dtype='DevDouble',
mandatory=True mandatory=True
) )
OPC_namespace = device_property(
dtype='DevString',
mandatory=False
)
# ---------- # ----------
# Attributes # Attributes
...@@ -85,15 +90,6 @@ class PCC(hardware_device): ...@@ -85,15 +90,6 @@ class PCC(hardware_device):
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 setup_value_dict(self):
""" set the initial value for all the attribute wrapper objects"""
self.value_dict = {str(i): i.initial_value() for i in self.attr_list()}
def always_executed_hook(self):
"""Method always executed before any TANGO command is executed."""
pass
def delete_device(self): def delete_device(self):
"""Hook to delete resources allocated in init_device. """Hook to delete resources allocated in init_device.
...@@ -116,15 +112,20 @@ class PCC(hardware_device): ...@@ -116,15 +112,20 @@ class PCC(hardware_device):
self.opcua_connection.stop() self.opcua_connection.stop()
def initialise(self): def initialise(self):
""" user code here. is called when the state is set to STANDBY """ """ user code here. is called when the state is set to INIT """
"""Initialises the attributes, values and properties of the PCC.""" # Init the dict that contains function to OPC-UA function mappings.
self.function_mapping = {}
# will contain all the values for this device self.function_mapping["RCU_on"] = {}
self.setup_value_dict() 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 #set up the OPC ua client
self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), self.OPC_Time_Out, self.Standby, 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.Standby, self.Fault, self)
# map the attributes to the OPC ua comm client # map the attributes to the OPC ua comm client
for i in self.attr_list(): for i in self.attr_list():
...@@ -135,6 +136,83 @@ class PCC(hardware_device): ...@@ -135,6 +136,83 @@ class PCC(hardware_device):
# -------- # --------
# Commands # Commands
# -------- # --------
@command()
@DebugIt()
@only_when_on
@fault_on_error
def RCU_off(self):
"""
:return:None
"""
self.function_mapping["RCU_off"]()
@command()
@DebugIt()
@only_when_on
@fault_on_error
def RCU_on(self):
"""
:return:None
"""
self.function_mapping["RCU_on"]()
@command()
@DebugIt()
@only_when_on
@fault_on_error
def ADC_on(self):
"""
:return:None
"""
self.function_mapping["ADC_on"]()
@command()
@DebugIt()
@only_when_on
@fault_on_error
def RCU_update(self):
"""
:return:None
"""
self.function_mapping["RCU_update"]()
@command()
@DebugIt()
@only_when_on
@fault_on_error
def CLK_off(self):
"""
:return:None
"""
self.function_mapping["CLK_off"]()
@command()
@DebugIt()
@only_when_on
@fault_on_error
def CLK_on(self):
"""
:return:None
"""
self.function_mapping["CLK_on"]()
@command()
@DebugIt()
@only_when_on
@fault_on_error
def CLK_PLL_setup(self):
"""
:return:None
"""
self.function_mapping["CLK_PLL_setup"]()
# ---------- # ----------
# Run server # Run server
......
...@@ -10,8 +10,7 @@ declare what client the attribute has to use in the initialisation and provide s ...@@ -10,8 +10,7 @@ declare what client the attribute has to use in the initialisation and provide s
To see how to add support for new clients, see `clients/README.md` To see how to add support for new clients, see `clients/README.md`
In addition it also provides an abstraction to the tango device, specifically for hardware devices. Examples of hardware devices In addition it also provides an abstraction to the tango device, specifically for hardware devices. Examples of hardware devices
can be found in SDP.py, PCC.py and test_device.py. as well as a completely empty template in HW_device_implementation.py can be found in TODO and an empty template can be found in `HW_device_tempalte.py`
Requires numpy Requires numpy
```pip install numpy``` ```pip install numpy```
...@@ -24,6 +23,4 @@ Requires pytango ...@@ -24,6 +23,4 @@ Requires pytango
### usage ### usage
You can start the device by calling it in any console with: You can start the device by calling it in any console with:
sdp.py instance_name <Device_name>.py instance_name
PCC.py instance_name
test_device.py instance_name
\ No newline at end of file
...@@ -73,9 +73,6 @@ class SDP(hardware_device): ...@@ -73,9 +73,6 @@ class SDP(hardware_device):
tr_tod_R = attribute_wrapper(comms_annotation=["1:tr_tod_R"], datatype=numpy.uint64) tr_tod_R = attribute_wrapper(comms_annotation=["1:tr_tod_R"], datatype=numpy.uint64)
tr_uptime_R = attribute_wrapper(comms_annotation=["1:tr_uptime_R"], datatype=numpy.uint64) tr_uptime_R = attribute_wrapper(comms_annotation=["1:tr_uptime_R"], datatype=numpy.uint64)
def setup_value_dict(self):
self.value_dict = {str(i): i.initial_value() for i in self.attr_list()}
def always_executed_hook(self): def always_executed_hook(self):
"""Method always executed before any TANGO command is executed.""" """Method always executed before any TANGO command is executed."""
pass pass
...@@ -106,7 +103,7 @@ class SDP(hardware_device): ...@@ -106,7 +103,7 @@ class SDP(hardware_device):
"""Initialises the attributes and properties of the PCC.""" """Initialises the attributes and properties of the PCC."""
# set up the OPC ua client # set up the OPC ua client
self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), self.OPC_Time_Out, self.Standby, 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.Standby, self.Fault, self)
# will contain all the values for this object # will contain all the values for this object
self.setup_value_dict() self.setup_value_dict()
......
# -*- coding: utf-8 -*-
#
# This file is part of the PCC project
#
#
#
# Distributed under the terms of the APACHE license.
# See LICENSE.txt for more info.
""" PCC Device Server for LOFAR2.0
"""
# PyTango imports
from tango.server import run
from tango.server import device_property
# Additional import
from clients.SNMP_client import SNMP_client
from src.attribute_wrapper import *
from src.hardware_device import *
__all__ = ["PCC", "main"]
class PCC(hardware_device):
"""
**Properties:**
- Device Property
SNMP_community
- Type:'DevString'
SNMP_host
- Type:'DevULong'
SNMP_timeout
- Type:'DevDouble'
"""
# -----------------
# Device Properties
# -----------------
SNMP_community = device_property(
dtype='DevString',
mandatory=True
)
SNMP_host = device_property(
dtype='DevString',
mandatory=True
)
SNMP_timeout = device_property(
dtype='DevDouble',
mandatory=True
)
# ----------
# Attributes
# ----------
attr0 = attribute_wrapper(comms_annotation={"oids": [""]}, datatype=numpy.bool_, dims=(32,), access=AttrWriteType.READ_WRITE)
attr1 = attribute_wrapper(comms_annotation={"oids": ["1.3.6.1.2.1.1.6.0"]}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE)
attr2 = attribute_wrapper(comms_annotation={"host": "127.0.0.1", "oids": ["1.3.6.1.2.1.1.5.0"]}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE)
attr3 = attribute_wrapper(comms_annotation={"host": "127.0.0.1", "oids": ["1.3.6.1.2.1.1.5.1", "1.3.6.1.2.1.1.5.2", "1.3.6.1.2.1.1.5.3"]}, dims=(3,), datatype=numpy.bool_)
attr4 = attribute_wrapper(comms_annotation={"host": "127.0.0.1", "oids": ["1.3.6.1.2.1.1.5.0"]}, dims=(3,), datatype=numpy.bool_)
# ["1.3.6.1.2.1.1.5.0"] gets transformed in to an array the size of dims with ".1", ".2" .. added
# ["1.3.6.1.2.1.1.5.0.1", "1.3.6.1.2.1.1.5.0.2", "1.3.6.1.2.1.1.5.0.3"]
def always_executed_hook(self):
"""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.")
# --------
# overloaded functions
# --------
def initialise(self):
""" user code here. is called when the state is set to STANDBY """
#set up the SNMP ua client
self.snmp_manager = SNMP_client(self.SNMP_community, self.SNMP_host, self.SNMP_timeout, self.Standby, self.Fault, self)
# map the attributes to the OPC ua comm client
for i in self.attr_list():
i.set_comm_client(self.OPCua_client)
self.OPCua_client.start()
# --------
# Commands
# --------
# ----------
# Run server
# ----------
def main(args=None, **kwargs):
"""Main function of the PCC module."""
return run((PCC,), args=args, **kwargs)
if __name__ == '__main__':
main()
from src.comms_client import *
import snmp
__all__ = ["SNMP_client"]
numpy_to_snmp_dict = {
"<class 'numpy.bool_'>": opcua.ua.VariantType.Boolean,
"<class 'numpy.int8'>": opcua.ua.VariantType.SByte,
"<class 'numpy.uint8'>": opcua.ua.VariantType.Byte,
"<class 'numpy.int16'>": opcua.ua.VariantType.Int16,
"<class 'numpy.uint16'>": opcua.ua.VariantType.UInt16,
"<class 'numpy.int32'>": opcua.ua.VariantType.Int32,
"<class 'numpy.uint32'>": opcua.ua.VariantType.UInt32,
"<class 'numpy.int64'>": opcua.ua.VariantType.Int64,
"<class 'numpy.uint64'>": opcua.ua.VariantType.UInt64,
"<class 'numpy.datetime_data'>": opcua.ua.VariantType.DateTime, # is this the right type, does it even matter?
"<class 'numpy.float32'>": opcua.ua.VariantType.Float,
"<class 'numpy.float64'>": opcua.ua.VariantType.Double,
"<class 'numpy.double'>": opcua.ua.VariantType.Double,
"<class 'numpy.str_'>": opcua.ua.VariantType.String,
"<class 'numpy.str'>": opcua.ua.VariantType.String,
"str": opcua.ua.VariantType.String
}
class SNMP_client(CommClient):
"""
messages to keep a check on the connection. On connection failure, reconnects once.
"""
def start(self):
super().start()
def __init__(self, community, host, timeout, on_func, fault_func, streams, try_interval=2):
"""
Create the SNMP and connect() to it
"""
super().__init__(on_func, fault_func, streams, try_interval)
self.community = community
self.host = host
self.manager = snmp.Manager(community, host, timeout)
# Explicitly connect
if not self.connect():
# hardware or infra is down -- needs fixing first
fault_func()
return
def connect(self):
"""
Try to connect to the client
"""
self.streams.debug_stream("Connecting to server %s %s", self.community, self.host)
self.connected = True
return True
def disconnect(self):
"""
disconnect from the client
"""
self.connected = False # always force a reconnect, regardless of a successful disconnect
def ping(self):
"""
ping the client to make sure the connection with the client is still functional.
"""
pass
def _setup_annotation(self, annotation):
"""
This class's Implementation of the get_mapping function. returns the read and write functions
"""
if isinstance(annotation, dict):
# check if required path inarg is present
if annotation.get('oids') is None:
AssertionError("SNMP get attributes require an oid")
oids = annotation.get("oids") # required
if annotation.get('host') is None:
AssertionError("SNMP get attributes require an host")
host = annotation.get("host") # required
else:
TypeError("SNMP attributes require a dict with oid and adress")
return
return host, oids
def setup_value_conversion(self, attribute):
"""
gives the client access to the attribute_wrapper object in order to access all data it could potentially need.
the OPC ua read/write functions require the dimensionality and the type to be known
"""
dim_x = attribute.dim_x
dim_y = attribute.dim_y
snmp_type = numpy_to_snmp_dict[str(attribute.numpy_type)] # convert the numpy type to a corresponding UA type
return dim_x, dim_y, snmp_type
def setup_attribute(self, annotation, attribute):
"""
MANDATORY function: is used by the attribute wrapper to get read/write functions. must return the read and write functions
"""
# process the annotation
host, oids = self._setup_annotation(annotation)
# get all the necessary data to set up the read/write functions from the attribute_wrapper
dim_x, dim_y, snmp_type = self.setup_value_conversion(attribute)
def _read_function(self):
vars = self.manager.get(host, *oids)
#TODO convert type
#todo
def _write_function(self, value):
self.manager.set(host, oids, value)
# return the read/write functions
return _read_function, _write_function
class snmp_get:
"""
This class provides a small wrapper for the OPC ua read/write functions in order to better organise the code
"""
def __init__(self, host, oid, dim_x, dim_y, snmp_type):
self.host = host
self.oid = oid
self.dim_y = dim_y
self.dim_x = dim_x
self.snmp_type = snmp_type
def read_function(self):
"""
Read_R function
"""
value = numpy.array(self.node.get_value())
if self.dim_y != 0:
value = numpy.array(numpy.split(value, indices_or_sections=self.dim_y))
else:
value = numpy.array(value)
return value
def write_function(self, value):
"""
write_RW function
"""
# set_data_value(opcua.ua.uatypes.Variant(value = value.tolist(), varianttype=opcua.ua.VariantType.Int32))
if self.dim_y != 0:
v = numpy.concatenate(value)
self.node.set_data_value(opcua.ua.uatypes.Variant(value=v.tolist(), varianttype=self.ua_type))
elif self.dim_x != 1:
self.node.set_data_value(opcua.ua.uatypes.Variant(value=value.tolist(), varianttype=self.ua_type))
else:
self.node.set_data_value(opcua.ua.uatypes.Variant(value=value, varianttype=self.ua_type))
...@@ -3,40 +3,40 @@ from src.comms_client import * ...@@ -3,40 +3,40 @@ from src.comms_client import *
__all__ = ["OPCUAConnection"] __all__ = ["OPCUAConnection"]
OPCua_to_numpy_dict = { # OPCua_to_numpy_dict = {
"VariantType.Boolean": numpy.bool_, # "VariantType.Boolean": numpy.bool_,
"VariantType.SByte": numpy.int8, # "VariantType.SByte": numpy.int8,
"VariantType.Byte": numpy.uint8, # "VariantType.Byte": numpy.uint8,
"VariantType.Int16": numpy.int16, # "VariantType.Int16": numpy.int16,
"VariantType.UInt16": numpy.uint16, # "VariantType.UInt16": numpy.uint16,
"VariantType.Int32": numpy.int32, # "VariantType.Int32": numpy.int32,
"VariantType.UInt32": numpy.uint32, # "VariantType.UInt32": numpy.uint32,
"VariantType.Int64": numpy.int64, # "VariantType.Int64": numpy.int64,
"VariantType.UInt64": numpy.uint64, # "VariantType.UInt64": numpy.uint64,
"VariantType.DateTime": numpy.datetime_data, # is this the right type, does it even matter? # "VariantType.DateTime": numpy.datetime_data, # is this the right type, does it even matter?
"VariantType.Float": numpy.float32, # "VariantType.Float": numpy.float32,
"VariantType.Double": numpy.double, # "VariantType.Double": numpy.double,
"VariantType.String": numpy.str, # "VariantType.String": numpy.str,
"VariantType.ByteString": numpy.uint8 # sequence of bytes, not a string # "VariantType.ByteString": numpy.uint8 # sequence of bytes, not a string
} # }
numpy_to_OPCua_dict = { numpy_to_OPCua_dict = {
"<class 'numpy.bool_'>": opcua.ua.VariantType.Boolean, numpy.bool_: opcua.ua.VariantType.Boolean,
"<class 'numpy.int8'>": opcua.ua.VariantType.SByte, numpy.int8: opcua.ua.VariantType.SByte,
"<class 'numpy.uint8'>": opcua.ua.VariantType.Byte, numpy.uint8: opcua.ua.VariantType.Byte,
"<class 'numpy.int16'>": opcua.ua.VariantType.Int16, numpy.int16: opcua.ua.VariantType.Int16,
"<class 'numpy.uint16'>": opcua.ua.VariantType.UInt16, numpy.uint16: opcua.ua.VariantType.UInt16,
"<class 'numpy.int32'>": opcua.ua.VariantType.Int32, numpy.int32: opcua.ua.VariantType.Int32,
"<class 'numpy.uint32'>": opcua.ua.VariantType.UInt32, numpy.uint32: opcua.ua.VariantType.UInt32,
"<class 'numpy.int64'>": opcua.ua.VariantType.Int64, numpy.int64: opcua.ua.VariantType.Int64,
"<class 'numpy.uint64'>": opcua.ua.VariantType.UInt64, numpy.uint64: opcua.ua.VariantType.UInt64,
"<class 'numpy.datetime_data'>": opcua.ua.VariantType.DateTime, # is this the right type, does it even matter? numpy.datetime_data: opcua.ua.VariantType.DateTime, # is this the right type, does it even matter?
"<class 'numpy.float32'>": opcua.ua.VariantType.Float, numpy.float32: opcua.ua.VariantType.Float,
"<class 'numpy.float64'>": opcua.ua.VariantType.Double, numpy.double: opcua.ua.VariantType.Double,
"<class 'numpy.double'>": opcua.ua.VariantType.Double, numpy.float64: opcua.ua.VariantType.Double,
"<class 'numpy.str_'>": opcua.ua.VariantType.String, numpy.str_: opcua.ua.VariantType.String,
"<class 'numpy.str'>": opcua.ua.VariantType.String, numpy.str: opcua.ua.VariantType.String,
"str": opcua.ua.VariantType.String str: opcua.ua.VariantType.String
} }
# <class 'numpy.bool_'> # <class 'numpy.bool_'>
...@@ -50,7 +50,7 @@ class OPCUAConnection(CommClient): ...@@ -50,7 +50,7 @@ class OPCUAConnection(CommClient):
def start(self): def start(self):
super().start() super().start()
def __init__(self, address, timeout, on_func, fault_func, streams, try_interval=2): def __init__(self, address, namespace, timeout, on_func, fault_func, streams, try_interval=2):
""" """
Create the OPC ua client and connect() to it and get the object node Create the OPC ua client and connect() to it and get the object node
""" """
...@@ -64,11 +64,13 @@ class OPCUAConnection(CommClient): ...@@ -64,11 +64,13 @@ class OPCUAConnection(CommClient):
fault_func() fault_func()
return return
self.streams.debug_stream("Demo ||\t connection established")
# determine namespace used # determine namespace used
try: try:
self.name_space_index = self.client.get_namespace_index("http://lofar.eu") if type(namespace) is str:
self.name_space_index = self.client.get_namespace_index(namespace)
elif type(namespace) is int:
self.name_space_index = namespace
except Exception as e: except Exception as e:
self.streams.warn_stream("Cannot determine the OPC-UA name space index. Will try and use the default = 2.") self.streams.warn_stream("Cannot determine the OPC-UA name space index. Will try and use the default = 2.")
self.name_space_index = 2 self.name_space_index = 2
...@@ -149,7 +151,7 @@ class OPCUAConnection(CommClient): ...@@ -149,7 +151,7 @@ class OPCUAConnection(CommClient):
dim_x = attribute.dim_x dim_x = attribute.dim_x
dim_y = attribute.dim_y dim_y = attribute.dim_y
ua_type = numpy_to_OPCua_dict[str(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 return dim_x, dim_y, ua_type
......
...@@ -6,24 +6,13 @@ import numpy ...@@ -6,24 +6,13 @@ import numpy
from src.wrappers import only_when_on, fault_on_error from src.wrappers import only_when_on, fault_on_error
def swap_dims_tuple(dims):
"""
arrays are inconsistent between tango and numpy.
This function exists to swap around the tuple containing the dimension data
"""
if len(dims) == 2:
return tuple((dims[1], dims[0]))
else:
return dims
class attribute_wrapper(attribute): 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,), **kwargs): 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 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. managing the communications interface.
...@@ -38,10 +27,7 @@ class attribute_wrapper(attribute): ...@@ -38,10 +27,7 @@ class attribute_wrapper(attribute):
self.comms_annotation = comms_annotation # store data that can be used by the comms interface. not used by the wrapper itself 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.numpy_type = datatype # tango changes our attribute to their representation (E.g numpy.int64 becomes "DevLong64")
wrap_RW = kwargs.get("access", AttrWriteType.READ) self.init_value = init_value
self.init_value = kwargs.get("init_value", None) # if not None, gets used as default value
max_dim_y = 0 max_dim_y = 0
# tango doesn't recognise numpy.str_, for consistencies sake we convert it here and hide this from the top level # tango doesn't recognise numpy.str_, for consistencies sake we convert it here and hide this from the top level
...@@ -52,10 +38,6 @@ class attribute_wrapper(attribute): ...@@ -52,10 +38,6 @@ class attribute_wrapper(attribute):
# check if not scalar # check if not scalar
if isinstance(dims, tuple): if isinstance(dims, tuple):
# fill the array with initial values
# self.value = numpy.zeros(swap_dims_tuple(dims), dtype=datatype)
# self.value = numpy.full(swap_dims_tuple(dims), datatype(0), dtype=data_type)
# get first dimension # get first dimension
max_dim_x = dims[0] max_dim_x = dims[0]
...@@ -72,7 +54,7 @@ class attribute_wrapper(attribute): ...@@ -72,7 +54,7 @@ class attribute_wrapper(attribute):
max_dim_x = 1 max_dim_x = 1
if wrap_RW == AttrWriteType.READ_WRITE: if access == AttrWriteType.READ_WRITE:
""" if the attribute is of READ_WRITE type, assign the RW and write function to it""" """ if the attribute is of READ_WRITE type, assign the RW and write function to it"""
@only_when_on @only_when_on
...@@ -83,7 +65,7 @@ class attribute_wrapper(attribute): ...@@ -83,7 +65,7 @@ class attribute_wrapper(attribute):
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: try:
return device.value_dict[str(self)] return device.value_dict[self]
except: except:
print() print()
...@@ -94,7 +76,7 @@ class attribute_wrapper(attribute): ...@@ -94,7 +76,7 @@ class attribute_wrapper(attribute):
_write_RW writes a value to this attribute _write_RW writes a value to this attribute
""" """
self.write_function(value) self.write_function(value)
device.value_dict[str(self)] = value device.value_dict[self] = value
self.fget = read_RW self.fget = read_RW
self.fset = write_RW self.fset = write_RW
...@@ -109,12 +91,12 @@ class attribute_wrapper(attribute): ...@@ -109,12 +91,12 @@ class attribute_wrapper(attribute):
""" """
_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[str(self)] = self.read_function() device.value_dict[self] = self.read_function()
return device.value_dict[str(self)] return device.value_dict[self]
self.fget = read_R self.fget = read_R
super().__init__(dtype=datatype, max_dim_y=max_dim_y, max_dim_x=max_dim_x, **kwargs) super().__init__(dtype=datatype, max_dim_y=max_dim_y, max_dim_x=max_dim_x, access=access, **kwargs)
return return
...@@ -131,7 +113,12 @@ class attribute_wrapper(attribute): ...@@ -131,7 +113,12 @@ class attribute_wrapper(attribute):
dims = (self.dim_x,) dims = (self.dim_x,)
# x and y are swapped for numpy and Tango. to maintain tango conventions, x and y are swapped for numpy # x and y are swapped for numpy and Tango. to maintain tango conventions, x and y are swapped for numpy
value = numpy.zeros(swap_dims_tuple(dims), dtype=self.numpy_type) if len(dims) == 2:
numpy_dims = tuple((dims[1], dims[0]))
else:
numpy_dims = dims
value = numpy.zeros(numpy_dims, dtype=self.numpy_type)
return value return value
def set_comm_client(self, client): def set_comm_client(self, client):
......
...@@ -54,6 +54,11 @@ class hardware_device(Device): ...@@ -54,6 +54,11 @@ class hardware_device(Device):
""" Return a list of all the attribute_wrapper members of this class. """ """ Return a list of all the attribute_wrapper members of this class. """
return [v for k, v in cls.__dict__.items() if type(v) == attribute_wrapper] return [v for k, v in cls.__dict__.items() if type(v) == attribute_wrapper]
def setup_value_dict(self):
""" set the initial value for all the attribute wrapper objects"""
self.value_dict = {i: i.initial_value() for i in self.attr_list()}
def init_device(self): def init_device(self):
""" Instantiates the device in the OFF state. """ """ Instantiates the device in the OFF state. """
...@@ -76,12 +81,10 @@ class hardware_device(Device): ...@@ -76,12 +81,10 @@ class hardware_device(Device):
:return:None :return:None
""" """
self.set_state(DevState.INIT) self.set_state(DevState.INIT)
self.initialise() self.setup_value_dict()
# if self.get_state() == DevState.STANDBY:
# # Already STANDBY. Don't complain. self.initialise()
# return
# self.set_state(DevState.STANDBY)
@only_in_states([DevState.INIT]) @only_in_states([DevState.INIT])
def Standby(self): def Standby(self):
......
...@@ -59,14 +59,6 @@ class test_device(hardware_device): ...@@ -59,14 +59,6 @@ class test_device(hardware_device):
float32_image_R = attribute_wrapper(comms_annotation="numpy.float32 type read image (dims = 8x2)", datatype=numpy.float32, dims=(8, 2)) 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)
tr_tod_R = attribute_wrapper(comms_annotation=["1:tr_tod_R"], datatype=numpy.uint64)
tr_uptime_R = attribute_wrapper(comms_annotation=["1:tr_uptime_R"], datatype=numpy.uint64)
def setup_value_dict(self):
""" set the initial value for all the attribute wrapper objects"""
self.value_dict = {str(i): i.initial_value() for i in self.attr_list()}
# -------- # --------
# overloaded functions # overloaded functions
# -------- # --------
...@@ -80,10 +72,6 @@ class test_device(hardware_device): ...@@ -80,10 +72,6 @@ class test_device(hardware_device):
#set up the OPC ua client #set up the OPC ua client
self.example_client = example_client(self.Standby, self.Fault, self) self.example_client = example_client(self.Standby, self.Fault, self)
# NOTE: MANDATORY will contain all attribute values for this tango device instance
self.setup_value_dict()
# map an access helper class # map an access helper class
for i in self.attr_list(): for i in self.attr_list():
i.set_comm_client(self.example_client) i.set_comm_client(self.example_client)
......
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