diff --git a/CDB/test_ConfigDb.json b/CDB/test_ConfigDb.json index 92331cd38297e3f014321c8565b1dca69f4a7572..879d73f275d0b7c275a01219cffcea92501be870 100644 --- a/CDB/test_ConfigDb.json +++ b/CDB/test_ConfigDb.json @@ -19,17 +19,36 @@ "LTS/SDP/1": { "properties": { "OPC_Server_Name": [ - "DESPi2.astron.nl" + "dop36.astron.nl" ] } } } } }, - "example_device": { + "APSCTL": { "1": { - "example_device": { - "LTS/example_device/1": { + "APSCTL": { + "LTS/APSCTL/1": { + "properties": { + "OPC_Server_Name": [ + "ltspi.astron.nl" + ], + "OPC_Server_Port": [ + "4844" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + } + } + } + }, + "test_device": { + "1": { + "test_device": { + "LTS/test_device/1": { "attribute_properties": { "Ant_mask_RW": { "archive_period": [ diff --git a/CDB/windows_ConfigDb.json b/CDB/windows_ConfigDb.json index 47c23ed110ba476e508bebe1ccf7bca1c387bd8d..c84fb3855372ba588de5bdef470d665b46ea6a99 100644 --- a/CDB/windows_ConfigDb.json +++ b/CDB/windows_ConfigDb.json @@ -26,10 +26,62 @@ } } }, - "example_device": { + "ini_device": { "1": { - "example_device": { - "LTS/example_device/1": { + "ini_device": { + "LTS/ini_device/1": { + "attribute_properties": { + "Ant_mask_RW": { + "archive_period": [ + "600000" + ] + } + }, + "properties": { + "SNMP_community": [ + "public" + ], + "SNMP_host": [ + "172.22.176.1" + ], + "SNMP_timeout": [ + "5.0" + ] + } + } + } + } + }, + "SNMP": { + "1": { + "SNMP": { + "LTS/SNMP/1": { + "attribute_properties": { + "Ant_mask_RW": { + "archive_period": [ + "600000" + ] + } + }, + "properties": { + "SNMP_community": [ + "public" + ], + "SNMP_host": [ + "172.22.176.1" + ], + "SNMP_timeout": [ + "5.0" + ] + } + } + } + } + }, + "test_device": { + "1": { + "test_device": { + "LTS/test_device/1": { "attribute_properties": { "Ant_mask_RW": { "archive_period": [ diff --git a/devices/APSCTL.py b/devices/APSCTL.py new file mode 100644 index 0000000000000000000000000000000000000000..e4c4dd38eaa9ab3aa18665093275749a54f3d94f --- /dev/null +++ b/devices/APSCTL.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the SDP project +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" SDP Device Server for LOFAR2.0 + +""" + +# PyTango imports +from tango.server import run +from tango.server import device_property +from tango import AttrWriteType + +#attribute extention and hardware device imports +from src.attribute_wrapper import attribute_wrapper +from src.hardware_device import hardware_device +import numpy +# Additional import + +from clients.opcua_connection import OPCUAConnection + + +__all__ = ["APSCTL", "main"] + +class APSCTL(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 + ) + + # ---------- + # Attributes + # ---------- + N_unb = 2 + N_fpga = 4 + N_ddr = 2 + N_qsfp = 6 + + + # Central CP per Uniboard + UNB2_Power_ON_OFF_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Power_ON_OFF_RW"], datatype=numpy.bool_, dims=(N_unb,), access=AttrWriteType.READ_WRITE) + UNB2_Front_Panel_LED_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Front_Panel_LED_RW"], datatype=numpy.uint8, dims=(N_unb,), access=AttrWriteType.READ_WRITE) + UNB2_Mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Mask_RW"], datatype=numpy.bool_, dims=(N_unb,), access=AttrWriteType.READ_WRITE) + # Central MP per Uniboard + UNB2_I2C_bus_OK_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_I2C_bus_OK_R"], datatype=numpy.bool_, dims=(N_unb,)) + UNB2_Front_Panel_LED_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Front_Panel_LED_R"], datatype=numpy.uint8, dims=(N_unb,)) + UNB2_EEPROM_Serial_Number_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_EEPROM_Serial_Number_R"], datatype=numpy.str, dims=(N_unb,)) + UNB2_EEPROM_Unique_ID_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_EEPROM_Unique_ID_R"], datatype=numpy.uint32, dims=(N_unb,)) + UNB2_DC_DC_48V_12V_VIN_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_VIN_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_DC_DC_48V_12V_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_DC_DC_48V_12V_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_DC_DC_48V_12V_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N01_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N01_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N01_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N01_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N01_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N01_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N23_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N23_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N23_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N23_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N23_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N23_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_1V2_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_1V2_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_1V2_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_1V2_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_1V2_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_1V2_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_PHY_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_PHY_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_PHY_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_PHY_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_PHY_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_PHY_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_CLOCK_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_CLOCK_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_CLOCK_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_CLOCK_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_CLOCK_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_CLOCK_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + + # monitor points per FPGA + UNB2_FPGA_DDR4_SLOT_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_DDR4_SLOT_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_DDR4_SLOT_PART_NUMBER_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_DDR4_SLOT_PART_NUMBER_R"], datatype=numpy.str, dims=(N_unb * N_qsfp,N_fpga)) + UNB2_FPGA_QSFP_CAGE_0_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_0_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_1_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_1_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_2_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_2_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_3_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_3_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_4_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_4_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_5_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_5_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_0_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_0_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_1_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_1_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_2_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_2_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_3_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_3_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_4_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_4_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga)) + UNB2_FPGA_QSFP_CAGE_5_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_5_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_CORE_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_CORE_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_CORE_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_ERAM_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_ERAM_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_ERAM_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_RXGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_RXGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_RXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_TXGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_TXGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_TXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_HGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_HGXB_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_HGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_HGXB_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_HGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_HGXB_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_PGM_VOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_PGM_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_PGM_IOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_PGM_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_FPGA_POL_PGM_TEMP_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_PGM_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + + + 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 off(self): + """ user code here. is called when the state is set to OFF """ + + # Stop keep-alive + self.opcua_connection.stop() + + def initialise(self): + """ user code here. is called when the sate is set to INIT """ + """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.Fault, self) + + # map an access helper class + for i in self.attr_list(): + try: + i.set_comm_client(self.OPCua_client) + except: + self.debug_stream("error in getting APSCTL attribute: {} from client".format(i)) + + self.OPCua_client.start() + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- +def main(args=None, **kwargs): + """Main function of the SDP module.""" + return run((APSCTL,), args=args, **kwargs) + + +if __name__ == '__main__': + main() + diff --git a/devices/HW_device_template.py b/devices/HW_device_template.py index c9365b496f7717d764b4f38d1011ece09a8d1665..cd7880672caf41e65db841408353896a795c5549 100644 --- a/devices/HW_device_template.py +++ b/devices/HW_device_template.py @@ -1,25 +1,25 @@ # -*- coding: utf-8 -*- # -# This file is part of the PCC project -# -# +# This file wraps around a tango device class and provides a number of abstractions useful for hardware devices. It works together # # Distributed under the terms of the APACHE license. # See LICENSE.txt for more info. -""" Hardware Device Server for LOFAR2.0 +""" """ # PyTango imports from tango.server import run +from tango import AttrWriteType # Additional import -from src.hardware_device import * - +from src.attribute_wrapper import attribute_wrapper +from src.hardware_device import hardware_device __all__ = ["HW_dev"] + class HW_dev(hardware_device): """ This class is the minimal (read empty) implementation of a class using 'hardware_device' @@ -29,7 +29,7 @@ class HW_dev(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 list (attr_list) for easy access example = attribute_wrapper(comms_annotation="this is an example", datatype=numpy.double, dims=(8, 2), access=AttrWriteType.READ_WRITE) ... @@ -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() - diff --git a/devices/LICENSE.txt b/devices/LICENSE.txt index c9978b8eee263aebfdd8d0a016e447940682ba8b..8a0eaeb196094a651006f51fd99c0c05cb16ccd6 100644 --- a/devices/LICENSE.txt +++ b/devices/LICENSE.txt @@ -1,201 +1,201 @@ - 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. + 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/PCC.py b/devices/PCC.py index 753b06285329bc50a980f795839c6f48056c73ef..96ef1013fa310d517fbf4637d1c423e08c10db15 100644 --- a/devices/PCC.py +++ b/devices/PCC.py @@ -15,21 +15,38 @@ from tango import DebugIt from tango.server import run, command from tango.server import device_property - +from tango import AttrWriteType +import numpy # Additional import + +from src.wrappers import * + from clients.opcua_connection import OPCUAConnection -from src.attribute_wrapper import * -from src.hardware_device import * +from src.attribute_wrapper import attribute_wrapper +from src.hardware_device import hardware_device 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 +61,7 @@ class PCC(hardware_device): dtype='DevDouble', mandatory=True ) - - OPC_Namespace = device_property( + OPC_namespace = device_property( dtype='DevString', mandatory=False ) @@ -53,56 +69,36 @@ 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. @@ -112,6 +108,7 @@ class PCC(hardware_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.") @@ -120,19 +117,25 @@ class PCC(hardware_device): # -------- def off(self): """ 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 +144,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() # -------- @@ -244,4 +237,3 @@ def main(args=None, **kwargs): if __name__ == '__main__': main() - diff --git a/devices/SDP.py b/devices/SDP.py index 10a1d8a5578b0df80cad13ef2dd2b9e9482d51c0..35c03232e8e0fb42b272f25325f20cad9ba03340 100644 --- a/devices/SDP.py +++ b/devices/SDP.py @@ -14,13 +14,16 @@ # PyTango imports from tango.server import run from tango.server import device_property +from tango import AttrWriteType # Additional import from clients.opcua_connection import OPCUAConnection -from src.attribute_wrapper import * -from src.hardware_device import * +from src.attribute_wrapper import attribute_wrapper +from src.hardware_device import hardware_device + from src.lofar_logging import device_logging_to_python +import numpy __all__ = ["SDP", "main"] @@ -105,10 +108,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(): diff --git a/devices/clients/opcua_connection.py b/devices/clients/opcua_connection.py index 676b24c650f0de251d850625fce1a47b1bc6518a..85962f4216013af69733eeab145ce2d39e460a5b 100644 --- a/devices/clients/opcua_connection.py +++ b/devices/clients/opcua_connection.py @@ -1,5 +1,9 @@ -from src.comms_client import * +from threading import Thread +import socket +from src.comms_client import CommClient +import numpy import opcua +from opcua import Client __all__ = ["OPCUAConnection"] @@ -33,13 +37,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 +81,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 +93,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 +118,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 +136,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 diff --git a/devices/clients/test_client.py b/devices/clients/test_client.py index 662bd692b462953414208c4cb1fc346474a2827b..b191d991ff85d3a201415224b0ee33572fcbd188 100644 --- a/devices/clients/test_client.py +++ b/devices/clients/test_client.py @@ -1,8 +1,11 @@ -from src.comms_client import * +from src.comms_client import CommClient +import numpy + +import os # <class 'numpy.bool_'> -class example_client(CommClient): +class test_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. @@ -11,11 +14,11 @@ class example_client(CommClient): def start(self): super().start() - def __init__(self, standby_func, fault_func, streams, try_interval=2): + def __init__(self, fault_func, streams, try_interval=2): """ initialises the class and tries to connect to the client. """ - super().__init__(standby_func, fault_func, streams, try_interval) + super().__init__(fault_func, streams, try_interval) # Explicitly connect if not self.connect(): @@ -27,11 +30,10 @@ class example_client(CommClient): """ 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 @@ -45,7 +47,9 @@ class example_client(CommClient): 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 + - server address + - IDs + - data structures """ # as this is an example, just print the annotation @@ -66,7 +70,6 @@ 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 @@ -85,7 +88,6 @@ 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. @@ -103,4 +105,3 @@ class example_client(CommClient): # return the read/write functions return read_function, write_function - diff --git a/devices/src/attribute_wrapper.py b/devices/src/attribute_wrapper.py index bdf457df41ba76c037de64faf4e99ecf4940ca70..98faf037c363fa92006bc656f821d636b5b396c0 100644 --- a/devices/src/attribute_wrapper.py +++ b/devices/src/attribute_wrapper.py @@ -5,28 +5,32 @@ 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. + + comms_annotation: data passed along to the attribute. can be given any form of data. handling is up to client implementation + datatype: any numpy datatype + dims: dimensions of the + init_value: value """ # ensure the type is a numpy array - if "numpy" not in str(datatype) and type(datatype) != str: + if "numpy" not in str(datatype) and 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 +58,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""" @@ -68,8 +71,8 @@ class attribute_wrapper(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 @@ -77,6 +80,7 @@ class attribute_wrapper(attribute): """ _write_RW writes a value to this attribute """ + self.write_function(value) device.value_dict[self] = value @@ -133,7 +137,8 @@ class attribute_wrapper(attribute): except Exception as e: def pass_func(value=None): pass - logger.error("setting comm_client failed. using pass function instead") + + logger.error("Exception while setting %s attribute with annotation: '%s' read/write functions. using pass function instead to to keep running", client.__class__.__name__, self.comms_annotation) self.read_function = pass_func self.write_function = pass_func diff --git a/devices/src/comms_client.py b/devices/src/comms_client.py index 6454ddffb14a5ab041ab8a3045ccf97e0226b0df..f189ce3fdec74bad5c40c2186bfc6b3f3c207cca 100644 --- a/devices/src/comms_client.py +++ b/devices/src/comms_client.py @@ -1,21 +1,15 @@ from threading import Thread -import socket import time -import numpy - -from tango import DevState - 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 +39,12 @@ 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() + 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. @@ -74,7 +66,7 @@ class CommClient(Thread): self.fault_func() def ping(self): - pass + return def stop(self): """ diff --git a/devices/src/hardware_device.py b/devices/src/hardware_device.py index a65f91287027608057657254693c01c2765270bc..431cbe9718fb3b586eea9a23aaa65aa1134fa59d 100644 --- a/devices/src/hardware_device.py +++ b/devices/src/hardware_device.py @@ -16,15 +16,14 @@ from tango.server import Device, command from tango import DevState, DebugIt # Additional import -from src.attribute_wrapper import * +from src.attribute_wrapper import attribute_wrapper from src.lofar_logging import log_exceptions - __all__ = ["hardware_device"] from src.wrappers import only_in_states - +#@log_exceptions() class hardware_device(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,19 +82,9 @@ 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) @command() @@ -155,8 +143,6 @@ class hardware_device(Device): pass def on(self): pass - def standby(self): - pass def initialise(self): pass @@ -164,7 +150,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. diff --git a/devices/test_device.py b/devices/test_device.py index ea7c333512b41d0dcb25efd1b1cbb556010f1881..38f688cc0cb715667b8237c4358f32024ced582a 100644 --- a/devices/test_device.py +++ b/devices/test_device.py @@ -7,8 +7,7 @@ # Distributed under the terms of the APACHE license. # See LICENSE.txt for more info. -""" PCC Device Server for LOFAR2.0 - +""" test Device Server """ # PyTango imports @@ -17,7 +16,7 @@ from tango.server import device_property from tango import DevState # Additional import -from clients.test_client import example_client +from clients.test_client import test_client from src.attribute_wrapper import * from src.hardware_device import * @@ -25,7 +24,6 @@ __all__ = ["test_device", "main"] class test_device(hardware_device): - # ----------------- # Device Properties # ----------------- @@ -48,16 +46,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,16 +70,15 @@ 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 test client + self.test_client = test_client(self.Fault, self) # map an access helper class for i in self.attr_list(): - i.set_comm_client(self.example_client) + i.set_comm_client(self.test_client) + self.test_client.start() - self.example_client.start() # ---------- # Run server diff --git a/docker-compose/jupyter/Dockerfile b/docker-compose/jupyter/Dockerfile index b9812188296627e9a5e2662007efa71ff78da90e..97ef7ca63daa60331ae0e8dee8f5d70fa143be44 100644 --- a/docker-compose/jupyter/Dockerfile +++ b/docker-compose/jupyter/Dockerfile @@ -25,3 +25,7 @@ ENV TINI_VERSION v0.6.0 ENV JUPYTER_RUNTIME_DIR=/tmp ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini RUN sudo chmod +x /usr/bin/tini + +# Make sure Jupyter can write to the home directory +ENV HOME=/home/tango +RUN chmod a+rwx /home/tango