diff --git a/devices/HW_device_template.py b/devices/HW_device_template.py index c9365b496f7717d764b4f38d1011ece09a8d1665..950faf39daf790962ef1f4cc3b93fb00382533fb 100644 --- a/devices/HW_device_template.py +++ b/devices/HW_device_template.py @@ -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() - 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..9e605cac2f33983c12d70f2a3d41fb6b2b9cd00f 100644 --- a/devices/PCC.py +++ b/devices/PCC.py @@ -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() - diff --git a/devices/SDP.py b/devices/SDP.py index 10a1d8a5578b0df80cad13ef2dd2b9e9482d51c0..879ed59436fbeb07a1f8b782816c44129001433f 100644 --- a/devices/SDP.py +++ b/devices/SDP.py @@ -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(): diff --git a/devices/clients/opcua_connection.py b/devices/clients/opcua_connection.py index 676b24c650f0de251d850625fce1a47b1bc6518a..d7094ef0249458c2e42d91e7acf9680784c580a2 100644 --- a/devices/clients/opcua_connection.py +++ b/devices/clients/opcua_connection.py @@ -1,5 +1,5 @@ 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 diff --git a/devices/clients/test_client.py b/devices/clients/test_client.py index 662bd692b462953414208c4cb1fc346474a2827b..ff85522d37518708831c6b0be4e9ba13b6128463 100644 --- a/devices/clients/test_client.py +++ b/devices/clients/test_client.py @@ -1,21 +1,22 @@ 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 - diff --git a/devices/lofar2_config.py b/devices/lofar2_config.py new file mode 100644 index 0000000000000000000000000000000000000000..581eea4f73a4d276613123ec9bf86bdb7e97a0ea --- /dev/null +++ b/devices/lofar2_config.py @@ -0,0 +1,13 @@ +#! /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) diff --git a/devices/src/attribute_wrapper.py b/devices/src/attribute_wrapper.py index bdf457df41ba76c037de64faf4e99ecf4940ca70..35670b0705e75668e628283198daba26107e78a2 100644 --- a/devices/src/attribute_wrapper.py +++ b/devices/src/attribute_wrapper.py @@ -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 diff --git a/devices/src/comms_client.py b/devices/src/comms_client.py index 6454ddffb14a5ab041ab8a3045ccf97e0226b0df..fc33bfad8cc4f5b88ac913b8c4bb294467ab775c 100644 --- a/devices/src/comms_client.py +++ b/devices/src/comms_client.py @@ -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. diff --git a/devices/src/hardware_device.py b/devices/src/hardware_device.py index a65f91287027608057657254693c01c2765270bc..0ee9d826bcac0004e7f53427482ffcdeb11fe86c 100644 --- a/devices/src/hardware_device.py +++ b/devices/src/hardware_device.py @@ -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. diff --git a/devices/startup.py b/devices/startup.py new file mode 100644 index 0000000000000000000000000000000000000000..f98097f994afc340fdb168311bcb524445658f1d --- /dev/null +++ b/devices/startup.py @@ -0,0 +1,37 @@ +#! /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 + diff --git a/devices/test_device.py b/devices/test_device.py index ea7c333512b41d0dcb25efd1b1cbb556010f1881..f84d810e724c4271d22d07d4e2af3e2e928a4153 100644 --- a/devices/test_device.py +++ b/devices/test_device.py @@ -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 # ----------