diff --git a/CDB/test_ConfigDb.json b/CDB/test_ConfigDb.json new file mode 100644 index 0000000000000000000000000000000000000000..92331cd38297e3f014321c8565b1dca69f4a7572 --- /dev/null +++ b/CDB/test_ConfigDb.json @@ -0,0 +1,56 @@ +{ + "servers": { + "PCC": { + "1": { + "PCC": { + "LTS/PCC/1": { + "properties": { + "OPC_Server_Name": [ + "ltspi.astron.nl" + ] + } + } + } + } + }, + "SDP": { + "1": { + "SDP": { + "LTS/SDP/1": { + "properties": { + "OPC_Server_Name": [ + "DESPi2.astron.nl" + ] + } + } + } + } + }, + "example_device": { + "1": { + "example_device": { + "LTS/example_device/1": { + "attribute_properties": { + "Ant_mask_RW": { + "archive_period": [ + "600000" + ] + } + }, + "properties": { + "OPC_Server_Name": [ + "host.docker.internal" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + } + } + } + } + } +} diff --git a/CDB/windows_ConfigDb.json b/CDB/windows_ConfigDb.json new file mode 100644 index 0000000000000000000000000000000000000000..47c23ed110ba476e508bebe1ccf7bca1c387bd8d --- /dev/null +++ b/CDB/windows_ConfigDb.json @@ -0,0 +1,56 @@ +{ + "servers": { + "PCC": { + "1": { + "PCC": { + "LTS/PCC/1": { + "properties": { + "OPC_Server_Name": [ + "host.docker.internal" + ] + } + } + } + } + }, + "SDP": { + "1": { + "SDP": { + "LTS/SDP/1": { + "properties": { + "OPC_Server_Name": [ + "host.docker.internal" + ] + } + } + } + } + }, + "example_device": { + "1": { + "example_device": { + "LTS/example_device/1": { + "attribute_properties": { + "Ant_mask_RW": { + "archive_period": [ + "600000" + ] + } + }, + "properties": { + "OPC_Server_Name": [ + "host.docker.internal" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + } + } + } + } + } +} diff --git a/PCC/test/test-PCC.py b/PCC/test/test-PCC.py old mode 100755 new mode 100644 diff --git a/devices/HW_device_template.py b/devices/HW_device_template.py new file mode 100644 index 0000000000000000000000000000000000000000..3d8b19c20b37cdf9b5d1d5780d28edad0c333f0d --- /dev/null +++ b/devices/HW_device_template.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the PCC project +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" Hardware Device Server for LOFAR2.0 + +""" + +# PyTango imports +from tango.server import run +# Additional import + +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' + """ + + # ---------- + # Attributes + # ---------- + """ + 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) + ... + + """ + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + pass + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command (a Tango built-in). + """ + self.debug_stream("Shutting down...") + + self.Off() + self.debug_stream("Shut down. Good bye.") + + # -------- + # overloaded functions + # -------- + def fault(self): + """ user code here. is called when the state is set to FAULT """ + pass + + def off(self): + """ user code here. is called when the state is set to OFF """ + pass + + def on(self): + """ user code here. is called when the state is set to ON """ + + pass + + def standby(self): + """ user code here. is called when the state is set to STANDBY """ + pass + + def initialise(self): + """ user code here. is called when the sate is set to INIT """ + pass + +# ---------- +# Run server +# ---------- +def main(args=None, **kwargs): + """Main function of the hardware device module.""" + return run((HW_dev,), args=args, **kwargs) + + +if __name__ == '__main__': + main() + diff --git a/devices/LICENSE.txt b/devices/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..8a0eaeb196094a651006f51fd99c0c05cb16ccd6 --- /dev/null +++ b/devices/LICENSE.txt @@ -0,0 +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. diff --git a/devices/PCC.py b/devices/PCC.py new file mode 100644 index 0000000000000000000000000000000000000000..7893073c1baa033ef1e5c9a1f85965ae127dc30b --- /dev/null +++ b/devices/PCC.py @@ -0,0 +1,231 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the PCC project +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" PCC Device Server for LOFAR2.0 + +""" + +# PyTango imports +from tango 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 * + + +__all__ = ["PCC", "main"] + +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 + ) + + OPC_Server_Port = device_property( + dtype='DevULong', + mandatory=True + ) + + OPC_Time_Out = device_property( + dtype='DevDouble', + mandatory=True + ) + OPC_namespace = device_property( + dtype='DevString', + mandatory=False + ) + + # ---------- + # Attributes + # ---------- + 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_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_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) + + 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). + """ + 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 state is set to INIT """ + + # 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.Standby, self.Fault, self) + + + # map the attributes to the OPC ua comm client + for i in self.attr_list(): + try: + i.set_comm_client(self.OPCua_client) + except: + pass + + self.OPCua_client.start() + + # -------- + # Commands + # -------- + @command() + @DebugIt() + @only_when_on + @fault_on_error + def RCU_off(self): + """ + + :return:None + """ + self.function_mapping["RCU_off"]() + + @command() + @DebugIt() + @only_when_on + @fault_on_error + def RCU_on(self): + """ + + :return:None + """ + self.function_mapping["RCU_on"]() + + @command() + @DebugIt() + @only_when_on + @fault_on_error + def ADC_on(self): + """ + + :return:None + """ + self.function_mapping["ADC_on"]() + + @command() + @DebugIt() + @only_when_on + @fault_on_error + def RCU_update(self): + """ + + :return:None + """ + self.function_mapping["RCU_update"]() + + @command() + @DebugIt() + @only_when_on + @fault_on_error + def CLK_off(self): + """ + + :return:None + """ + self.function_mapping["CLK_off"]() + + @command() + @DebugIt() + @only_when_on + @fault_on_error + def CLK_on(self): + """ + + :return:None + """ + self.function_mapping["CLK_on"]() + + @command() + @DebugIt() + @only_when_on + @fault_on_error + def CLK_PLL_setup(self): + """ + + :return:None + """ + self.function_mapping["CLK_PLL_setup"]() + + +# ---------- +# Run server +# ---------- +def main(args=None, **kwargs): + """Main function of the PCC module.""" + return run((PCC,), args=args, **kwargs) + + +if __name__ == '__main__': + main() + diff --git a/devices/README.md b/devices/README.md new file mode 100644 index 0000000000000000000000000000000000000000..99772cd2e7e076a62201d4b47e525f4bf4617582 --- /dev/null +++ b/devices/README.md @@ -0,0 +1,26 @@ +# Device wrapper + +This code provides an attribute_wrapper class in place of attributes for tango devices. the attribute wrappers contain additional code +that moves a lot of the complexity and redundant code to the background. + +The tango Device class is also abstracted further to a "hardware_device" class. This class wraps + +The only things required on the users part are to declare the attributes using the attribute_wrapper (see `example/example_device`), +declare what client the attribute has to use in the initialisation and provide support for the used clients. +To see how to add support for new clients, see `clients/README.md` + +In addition it also provides an abstraction to the tango device, specifically for hardware devices. Examples of hardware devices +can be found in TODO and an empty template can be found in `HW_device_template.py` + +Requires numpy +```pip install numpy``` + +Requires opcua +```pip install opcua``` + +Requires pytango +```pip install pytango``` + +### usage +You can start the device by calling it in any console with: +<Device_name>.py instance_name diff --git a/devices/SDP.py b/devices/SDP.py new file mode 100644 index 0000000000000000000000000000000000000000..2f54bed7bc727323eed635b81880ad4b783f19c0 --- /dev/null +++ b/devices/SDP.py @@ -0,0 +1,131 @@ +# -*- 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 +# Additional import + +from clients.opcua_connection import OPCUAConnection +from src.attribute_wrapper import * +from src.hardware_device import * + + +__all__ = ["SDP", "main"] + +class SDP(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 + # ---------- + fpga_mask_RW = attribute_wrapper(comms_annotation=["1:fpga_mask_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) + fpga_scrap_R = attribute_wrapper(comms_annotation=["1:fpga_scrap_R"], datatype=numpy.int32, dims=(2048,)) + fpga_scrap_RW = attribute_wrapper(comms_annotation=["1:fpga_scrap_RW"], datatype=numpy.int32, dims=(2048,), access=AttrWriteType.READ_WRITE) + fpga_status_R = attribute_wrapper(comms_annotation=["1:fpga_status_R"], datatype=numpy.bool_, dims=(16,)) + fpga_temp_R = attribute_wrapper(comms_annotation=["1:fpga_temp_R"], datatype=numpy.float_, dims=(16,)) + fpga_version_R = attribute_wrapper(comms_annotation=["1:fpga_version_R"], datatype=numpy.str_, dims=(16,)) + fpga_weights_R = attribute_wrapper(comms_annotation=["1:fpga_weights_R"], datatype=numpy.int16, dims=(16, 12 * 488 * 2)) + fpga_weights_RW = attribute_wrapper(comms_annotation=["1:fpga_weights_RW"], datatype=numpy.int16, dims=(16, 12 * 488 * 2), access=AttrWriteType.READ_WRITE) + tr_busy_R = attribute_wrapper(comms_annotation=["1:tr_busy_R"], datatype=numpy.bool_) + # NOTE: typo in node name is 'tr_reload_W' should be 'tr_reload_RW' + tr_reload_RW = attribute_wrapper(comms_annotation=["1:tr_reload_W"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + tr_tod_R = attribute_wrapper(comms_annotation=["1:tr_tod_R"], datatype=numpy.uint64) + tr_uptime_R = attribute_wrapper(comms_annotation=["1:tr_uptime_R"], datatype=numpy.uint64) + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + pass + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command (a Tango built-in). + """ + self.debug_stream("Shutting down...") + + self.Off() + self.debug_stream("Shut down. Good bye.") + + # -------- + # overloaded functions + # -------- + def 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.Standby, self.Fault, self) + + # will contain all the values for this object + self.setup_value_dict() + + # map an access helper class + for i in self.attr_list(): + i.set_comm_client(self.OPCua_client) + + self.OPCua_client.start() + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- +def main(args=None, **kwargs): + """Main function of the SDP module.""" + return run((SDP,), args=args, **kwargs) + + +if __name__ == '__main__': + main() + diff --git a/devices/clients/README.md b/devices/clients/README.md new file mode 100644 index 0000000000000000000000000000000000000000..34d2709a0c70ab960b4ac23cb19668fab689bc7c --- /dev/null +++ b/devices/clients/README.md @@ -0,0 +1,4 @@ +this folder contains all the comms_client implementations for organisation + +### How to add a new client +soon™ \ No newline at end of file diff --git a/devices/clients/opcua_connection.py b/devices/clients/opcua_connection.py new file mode 100644 index 0000000000000000000000000000000000000000..1d1b1004c2a193d07542c6cdf8237da94ea9352b --- /dev/null +++ b/devices/clients/opcua_connection.py @@ -0,0 +1,204 @@ +from src.comms_client import * + + +__all__ = ["OPCUAConnection"] + +numpy_to_OPCua_dict = { + numpy.bool_: opcua.ua.VariantType.Boolean, + numpy.int8: opcua.ua.VariantType.SByte, + numpy.uint8: opcua.ua.VariantType.Byte, + numpy.int16: opcua.ua.VariantType.Int16, + numpy.uint16: opcua.ua.VariantType.UInt16, + numpy.int32: opcua.ua.VariantType.Int32, + numpy.uint32: opcua.ua.VariantType.UInt32, + numpy.int64: opcua.ua.VariantType.Int64, + numpy.uint64: opcua.ua.VariantType.UInt64, + numpy.datetime_data: opcua.ua.VariantType.DateTime, # is this the right type, does it even matter? + numpy.float32: opcua.ua.VariantType.Float, + numpy.double: opcua.ua.VariantType.Double, + numpy.float64: opcua.ua.VariantType.Double, + numpy.str_: opcua.ua.VariantType.String, + numpy.str: opcua.ua.VariantType.String, + str: opcua.ua.VariantType.String +} + +# <class 'numpy.bool_'> + +class OPCUAConnection(CommClient): + """ + Connects to OPC-UA in the foreground or background, and sends HELLO + messages to keep a check on the connection. On connection failure, reconnects once. + """ + + def start(self): + super().start() + + def __init__(self, address, namespace, timeout, on_func, fault_func, streams, try_interval=2): + """ + Create the OPC ua client and connect() to it and get the object node + """ + super().__init__(on_func, fault_func, streams, try_interval) + + self.client = Client(address, timeout) + + # Explicitly connect + if not self.connect(): + # hardware or infra is down -- needs fixing first + fault_func() + return + + # determine namespace used + try: + if type(namespace) is str: + self.name_space_index = self.client.get_namespace_index(namespace) + elif type(namespace) is int: + self.name_space_index = namespace + + except Exception as e: + #TODO remove once SDP is fixed + self.streams.warn_stream("Cannot determine the OPC-UA name space index. Will try and use the default = 2.") + self.name_space_index = 2 + + self.obj = self.client.get_objects_node() + + def _servername(self): + return self.client.server_url.geturl() + + def connect(self): + """ + Try to connect to the client + """ + + try: + self.streams.debug_stream("Connecting to server %s", self._servername()) + self.client.connect() + self.connected = True + self.streams.debug_stream("Connected to %s. Initialising.", self._servername()) + return True + except socket.error as e: + #TODO + self.streams.error_stream("Could not connect to server %s: %s", self._servername()) + raise Exception("") from e + + + def disconnect(self): + """ + disconnect from the client + """ + self.connected = False # always force a reconnect, regardless of a successful disconnect + + try: + self.client.disconnect() + except Exception as 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: + self.client.send_hello() + except Exception as e: + raise Exception("Lost connection to server %s: %s", self._servername(), e) + + def _setup_annotation(self, annotation): + """ + This class's Implementation of the get_mapping function. returns the read and write functions + """ + + if isinstance(annotation, dict): + # check if required path inarg is present + if annotation.get('path') is None: + raise Exception("OPC-ua mapping requires a path argument in the annotation, was given: %s", annotation) + + path = annotation.get("path") # required + 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 %s type containing: %s", type(annotation), annotation) + + try: + node = self.obj.get_child(path) + except Exception as 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 + + def setup_value_conversion(self, attribute): + """ + gives the client access to the attribute_wrapper object in order to access all data it could potentially need. + the OPC ua read/write functions require the dimensionality and the type to be known + """ + + dim_x = attribute.dim_x + dim_y = attribute.dim_y + 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 + + def setup_attribute(self, annotation, attribute): + """ + MANDATORY function: is used by the attribute wrapper to get read/write functions. must return the read and write functions + """ + + # process the annotation + node = self._setup_annotation(annotation) + + # get all the necessary data to set up the read/write functions from the attribute_wrapper + dim_x, dim_y, ua_type = self.setup_value_conversion(attribute) + + # configure and return the read/write functions + prot_attr = ProtocolAttribute(node, dim_x, dim_y, ua_type) + + try: + # NOTE: debug statement tries to get the qualified name, this may not always work. in that case forgo the name and just print the path + node_name = str(node.get_browse_name())[len("QualifiedName(2:"):] + self.streams.debug_stream("connected OPC ua node {} of type {} to attribute with dimensions: {} x {} ".format(str(node_name)[:len(node_name)-1], str(ua_type)[len("VariantType."):], dim_x, dim_y)) + except: + pass + + # return the read/write functions + return prot_attr.read_function, prot_attr.write_function + + +class ProtocolAttribute: + """ + This class provides a small wrapper for the OPC ua read/write functions in order to better organise the code + """ + + def __init__(self, node, dim_x, dim_y, ua_type): + self.node = node + self.dim_y = dim_y + self.dim_x = dim_x + self.ua_type = ua_type + + def read_function(self): + """ + Read_R function + """ + value = numpy.array(self.node.get_value()) + + if self.dim_y != 0: + value = numpy.array(numpy.split(value, indices_or_sections=self.dim_y)) + else: + value = numpy.array(value) + return value + + def write_function(self, value): + """ + write_RW function + """ + # set_data_value(opcua.ua.uatypes.Variant(value = value.tolist(), varianttype=opcua.ua.VariantType.Int32)) + + if self.dim_y != 0: + v = numpy.concatenate(value) + self.node.set_data_value(opcua.ua.uatypes.Variant(value=v.tolist(), varianttype=self.ua_type)) + + elif self.dim_x != 1: + self.node.set_data_value(opcua.ua.uatypes.Variant(value=value.tolist(), varianttype=self.ua_type)) + else: + self.node.set_data_value(opcua.ua.uatypes.Variant(value=value, varianttype=self.ua_type)) + + + diff --git a/devices/clients/test_client.py b/devices/clients/test_client.py new file mode 100644 index 0000000000000000000000000000000000000000..6bac45275275a86156a518286e353e7f844f9f02 --- /dev/null +++ b/devices/clients/test_client.py @@ -0,0 +1,106 @@ +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. + """ + + 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. + """ + super().__init__(standby_func, fault_func, streams, try_interval) + + # Explicitly connect + if not self.connect(): + # hardware or infra is down -- needs fixing first + fault_func() + return + + def connect(self): + """ + 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 + + def disconnect(self): + self.connected = False # always force a reconnect, regardless of a successful disconnect + self.streams.debug_stream("disconnected from the 'client' ") + + 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. + + 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 + """ + + if attribute.dim_y > 1: + dims = (attribute.dim_y, attribute.dim_x) + else: + dims = (attribute.dim_x,) + + dtype = attribute.numpy_type + + return dims, dtype + + + def _setup_mapping(self, dims, dtype): + """ + takes all gathered data to configure and return the correct read and write functions + """ + + value = numpy.zeros(dims, dtype) + + def read_function(): + self.streams.debug_stream("from read_function, reading {} array of type {}".format(dims, dtype)) + return value + + def write_function(write_value): + self.streams.debug_stream("from write_function, writing {} array of type {}".format(dims, dtype)) + value = write_value + + 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 + """ + + # process the comms_annotation + self._setup_annotation(annotation) + + # get all the necessary data to set up the read/write functions from the attribute_wrapper + dims, dtype = self._setup_value_conversion(attribute) + + # configure and return the read/write functions + read_function, write_function = self._setup_mapping(dims, dtype) + + # return the read/write functions + return read_function, write_function + diff --git a/devices/src/attribute_wrapper.py b/devices/src/attribute_wrapper.py new file mode 100644 index 0000000000000000000000000000000000000000..c1b185038853b9b833e35dd3487ae6ad7c4b461a --- /dev/null +++ b/devices/src/attribute_wrapper.py @@ -0,0 +1,141 @@ +from tango.server import attribute +from tango import AttrWriteType + +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 + """ + + 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. + """ + + # 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.init_value = init_value + max_dim_y = 0 + + # tango doesn't recognise numpy.str_, for consistencies sake we convert it here and hide this from the top level + # NOTE: discuss, idk if this is an important detail somewhere else + if datatype is numpy.str_: + datatype = str + + # check if not scalar + if isinstance(dims, tuple): + + # get first dimension + max_dim_x = dims[0] + + # single dimension/spectrum requires the datatype to be wrapped in a tuple + datatype = (datatype,) + + if len(dims) == 2: + # get second dimension + max_dim_y = dims[1] + # wrap the datatype tuple in another tuple for 2d arrays/images + datatype = (datatype,) + else: + # 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""" + + @only_when_on + @fault_on_error + 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 + """ + 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 + + + @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 + + self.fget = read_RW + self.fset = write_RW + + + else: + """ if the attribute is of READ type, assign the read function to it""" + + @only_when_on + @fault_on_error + def read_R(device): + """ + _read_R reads the attribute value, stores it and returns it" + """ + device.value_dict[self] = self.read_function() + return device.value_dict[self] + + self.fget = read_R + + super().__init__(dtype=datatype, max_dim_y=max_dim_y, max_dim_x=max_dim_x, access=access, **kwargs) + + return + + 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 + """ + if self.init_value is not None: + return self.init_value + + if self.dim_y > 1: + dims = (self.dim_x, self.dim_y) + else: + dims = (self.dim_x,) + + # x and y are swapped for numpy and Tango. to maintain tango conventions, x and y are swapped for numpy + if len(dims) == 2: + numpy_dims = tuple((dims[1], dims[0])) + else: + numpy_dims = dims + + value = numpy.zeros(numpy_dims, dtype=self.numpy_type) + return value + + 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. + """ + 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 + self.write_function = pass_func + + raise Exception("Exception while setting comm_client read/write functions. using pass function instead. %s") from e diff --git a/devices/src/comms_client.py b/devices/src/comms_client.py new file mode 100644 index 0000000000000000000000000000000000000000..672ec4c399f22890cf40779edb35291e4cb0da89 --- /dev/null +++ b/devices/src/comms_client.py @@ -0,0 +1,139 @@ +from threading import Thread +import socket +import time +import numpy + +import opcua +from opcua import Client + +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): + """ + + """ + self.standby_func = standby_func + self.fault_func = fault_func + self.try_interval = try_interval + self.streams = streams + self.stopping = False + self.connected = False + + super().__init__(daemon=True) + + def connect(self): + """ + Function used to connect to the client. + """ + self.connected = True + return True + + def disconnect(self): + """ + Function used to connect to the client. + """ + self.connected = False + + def run(self): + + # Explicitly connect + if not self.connect(): + # hardware or infra is down -- needs fixing first + 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() + 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. + self.fault_func() + return + + # keep checking if the connection is still alive + try: + while not self.stopping: + self.ping() + time.sleep(self.try_interval) + except Exception as e: + self.streams.error_stream("Fault condition in communication detected.", e) + + # technically, we may not have dropped the connection, but encounter a different error. so explicitly disconnect. + self.disconnect() + + # signal that we're disconnected + self.fault_func() + + def ping(self): + pass + + def stop(self): + """ + Stop connecting & disconnect. Can take a few seconds for the timeouts to hit. + """ + + if not self.ident: + # have not yet been started, so nothing to do + return + + self.stopping = True + self.join() + + self.disconnect() + + def setup_attribute(self, annotation, attribute): + """ + This function is responsible for providing the attribute_wrapper with a read/write function + How this is done is implementation specific. + The setup-attribute has access to the comms_annotation provided to the attribute wrapper to pass along to the comms client + as well as a reference to the attribute itself. + + It should do this by first calling: _setup_annotation and setup_value_conversion to get all data necceacry to configure the read/write functions. + It should then return the read and write functions to the attribute. + + MANDATORY: + annotation_outputs = _setup_annotation(annotation) + attribute_outputs = _setup_annotation(attribute) + (note: outputs are up to the user) + + REQUIRED: provide read and write functions to return, there are no restrictions on how these should be provided, + except that the read function takes a single input value and the write function returns a single value + + MANDATORY: + return read_function, write_function + + Examples: + - File system: get_mapping returns functions that read/write a fixed + number of bytes at a fixed location in a file. (SEEK) + - OPC-UA: traverse the OPC-UA tree until the node is found. + Then return the read/write functions for that node which automatically + convert values between Python and OPC-UA. + """ + raise NotImplementedError("the setup_attribute must be implemented and provide return a valid read/write function for the attribute") + + def _setup_annotation(self, annotation): + """ + This function is responsible for handling the annotation data provided by the attribute to configure the read/write function the client must provide. + This function should be called by setup_attribute + """ + raise NotImplementedError("the _setup_annotation must be implemented, content and outputs are up to the user") + + def setup_value_conversion(self, attribute): + """ + this function is responsible for setting up the value conversion between the client and the attribute. + This function should be called by setup_attribute + """ + raise NotImplementedError("the setup_value_conversion must be implemented, content and outputs are up to the user") + diff --git a/devices/src/hardware_device.py b/devices/src/hardware_device.py new file mode 100644 index 0000000000000000000000000000000000000000..0b9d488fb5f9df78d0768923c0b360c034b06acc --- /dev/null +++ b/devices/src/hardware_device.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the PCC project +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" PCC Device Server for LOFAR2.0 + +""" + +# PyTango imports +from tango.server import Device, command +from tango import DevState, DebugIt +# Additional import + +from src.attribute_wrapper import * + + +__all__ = ["hardware_device"] + +from src.wrappers import only_in_states + + +class hardware_device(Device): + """ + + **Properties:** + + States are as follows: + INIT = Device is initialising. + STANDBY = Device is initialised, but pends external configuration and an explicit turning on, + ON = Device is fully configured, functional, controls the hardware, and is possibly actively running, + FAULT = Device detected an unrecoverable error, and is thus malfunctional, + OFF = Device is turned off, drops connection to the hardware, + + The following state transitions are implemented: + boot -> OFF: Triggered by tango. Device will be instantiated, + OFF -> INIT: Triggered by device. Device will initialise (connect to hardware, other devices), + INIT -> STANDBY: Triggered by device. Device is initialised, and is ready for additional configuration by the user, + STANDBY -> ON: Triggered by user. Device reports to be functional, + * -> FAULT: Triggered by device. Device has degraded to malfunctional, for example because the connection to the hardware is lost, + * -> FAULT: Triggered by user. Emulate a forced malfunction for integration testing purposes, + * -> OFF: Triggered by user. Device is turned off. Triggered by the Off() command, + FAULT -> INIT: Triggered by user. Device is reinitialised to recover from an error, + + The user triggers their transitions by the commands reflecting the target state (Initialise(), On(), Fault()). + """ + + @classmethod + def attr_list(cls): + """ Return a list of all the attribute_wrapper members of this class. """ + return [v for k, v in cls.__dict__.items() if type(v) == attribute_wrapper] + + def setup_value_dict(self): + """ set the initial value for all the attribute wrapper objects""" + + self.value_dict = {i: i.initial_value() for i in self.attr_list()} + + def init_device(self): + """ Instantiates the device in the OFF state. """ + + # NOTE: Will delete_device first, if necessary + Device.init_device(self) + + self.set_state(DevState.OFF) + + # -------- + # Commands + # -------- + + @command() + @only_in_states([DevState.FAULT, DevState.OFF]) + @DebugIt() + def Initialise(self): + """ + Command to ask for initialisation of this device. Can only be called in FAULT or OFF state. + + :return:None + """ + self.set_state(DevState.INIT) + self.setup_value_dict() + + + self.initialise() + + @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() + def On(self): + """ + Command to ask for initialisation of this device. Can only be called in FAULT or OFF state. + + :return:None + """ + self.on() + self.set_state(DevState.ON) + + @command() + @DebugIt() + def Off(self): + """ + Command to ask for shutdown of this device. + + :return:None + """ + if self.get_state() == DevState.OFF: + # Already off. Don't complain. + return + + # Turn off + self.set_state(DevState.OFF) + + self.off() + + # Turn off again, in case of race conditions through reconnecting + self.set_state(DevState.OFF) + + @command() + @only_in_states([DevState.ON, DevState.INIT, DevState.STANDBY]) + @DebugIt() + def Fault(self): + """ + FAULT state is used to indicate our connection with the OPC-UA server is down. + + This device will try to reconnect once, and transition to the ON state on success. + + If reconnecting fails, the user needs to call Initialise() to retry to restart this device. + + :return:None + """ + self.fault() + self.set_state(DevState.FAULT) + + + # functions that can be overloaded + def fault(self): + pass + def off(self): + pass + def on(self): + pass + def standby(self): + pass + def initialise(self): + pass + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + pass + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command (a Tango built-in). + """ + self.debug_stream("Shutting down...") + + self.Off() + self.debug_stream("Shut down. Good bye.") diff --git a/devices/src/wrappers.py b/devices/src/wrappers.py new file mode 100644 index 0000000000000000000000000000000000000000..9dbc45a68dc850b36bd30a0a5b8664d104b58e30 --- /dev/null +++ b/devices/src/wrappers.py @@ -0,0 +1,53 @@ +from tango import DevState, Except +from functools import wraps +import traceback + +__all__ = ["only_in_states", "only_when_on", "fault_on_error"] + +def only_in_states(allowed_states): + """ + Wrapper to call and return the wrapped function if the device is + in one of the given states. Otherwise a PyTango exception is thrown. + """ + def wrapper(func): + @wraps(func) + def state_check_wrapper(self, *args, **kwargs): + if self.get_state() in allowed_states: + return func(self, *args, **kwargs) + + self.warn_stream("Illegal command: Function %s can only be called in states %s. Current state: %s" % (func.__name__, allowed_states, self.get_state())) + Except.throw_exception("IllegalCommand", "Function can only be called in states %s. Current state: %s" % (allowed_states, self.get_state()), func.__name__) + + return state_check_wrapper + + return wrapper + +def only_when_on(func): + """ + Wrapper to call and return the wrapped function if the device is + in the ON state. Otherwise None is returned and nothing + will be called. + """ + @wraps(func) + def when_on_wrapper(self, *args, **kwargs): + if self.get_state() == DevState.ON: + return func(self, *args, **kwargs) + + return None + + return when_on_wrapper + +def fault_on_error(func): + """ + Wrapper to catch exceptions. Sets the device in a FAULT state if any occurs. + """ + @wraps(func) + def error_wrapper(self, *args, **kwargs): + try: + return func(self, *args, **kwargs) + except Exception as e: + self.error_stream("Function failed. Trace: %s", traceback.format_exc()) + self.Fault() + return None + + return error_wrapper diff --git a/devices/test_device.py b/devices/test_device.py new file mode 100644 index 0000000000000000000000000000000000000000..27bdbfcb079e5019be6826ad8e7d3354ac296722 --- /dev/null +++ b/devices/test_device.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the PCC project +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" PCC Device Server for LOFAR2.0 + +""" + +# PyTango imports +from tango.server import run +from tango.server import device_property +from tango import DevState +# Additional import + +from clients.test_client import example_client +from src.attribute_wrapper import * +from src.hardware_device import * + +__all__ = ["test_device", "main"] + + +class test_device(hardware_device): + + # ----------------- + # Device Properties + # ----------------- + + OPC_Server_Name = device_property( + dtype='DevString', + ) + + OPC_Server_Port = device_property( + dtype='DevULong', + ) + + OPC_Time_Out = device_property( + dtype='DevDouble', + ) + + # ---------- + # Attributes + # ---------- + 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) + + 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) + + 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) + 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) + + # -------- + # overloaded functions + # -------- + def initialise(self): + """ user code here. is called when the sate is set to INIT """ + """Initialises the attributes and properties of the PCC.""" + + self.set_state(DevState.INIT) + + + #set up the OPC ua client + self.example_client = example_client(self.Standby, 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 +# ---------- +def main(args=None, **kwargs): + """Main function of the example module.""" + return run((test_device,), args=args, **kwargs) + + +if __name__ == '__main__': + main() diff --git a/jupyter-notebooks/PCC_notebook.ipynb b/jupyter-notebooks/PCC_notebook.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..f0dd0f9bedae6261c0524b03898491b72eeac1b2 --- /dev/null +++ b/jupyter-notebooks/PCC_notebook.ipynb @@ -0,0 +1,173 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "funded-deputy", + "metadata": {}, + "outputs": [], + "source": [ + "import time" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "bridal-mumbai", + "metadata": {}, + "outputs": [], + "source": [ + "d=DeviceProxy(\"LTS/PCC/1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "subjective-conference", + "metadata": {}, + "outputs": [], + "source": [ + "state = str(d.state())\n", + "\n", + "if state == \"OFF\":\n", + " d.initialise()\n", + " time.sleep(1)\n", + "state = str(d.state())\n", + "if state == \"STANDBY\":\n", + " d.on()\n", + "state = str(d.state())\n", + "if state == \"ON\":\n", + " print(\"Device is now in on state\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "liable-thesaurus", + "metadata": {}, + "outputs": [ + { + "ename": "DevFailed", + "evalue": "DevFailed[\nDevError[\n desc = Read value for attribute RCU_mask_RW has not been updated\n origin = Device_3Impl::read_attributes_no_except\n reason = API_AttrValueNotSet\nseverity = ERR]\n\nDevError[\n desc = Failed to read_attribute on device lts/pcc/1, attribute RCU_mask_RW\n origin = DeviceProxy::read_attribute()\n reason = API_AttributeFailed\nseverity = ERR]\n]", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDevFailed\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-4-aafae2adcd98>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m values = [[d.RCU_mask_RW, \"RCU_mask_RW\"],\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mAnt_mask_RW\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\"Ant_mask_RW\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mRCU_attenuator_R\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\"RCU_attenuator_R\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mRCU_attenuator_RW\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\"RCU_attenuator_RW\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mRCU_band_R\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\"RCU_band_R\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__DeviceProxy__getattr\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 342\u001b[0m \u001b[0mattr_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__get_attr_cache\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname_l\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 343\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 344\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m__get_attribute_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 345\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 346\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__get_attribute_value\u001b[0;34m(self, attr_info, name)\u001b[0m\n\u001b[1;32m 281\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__get_attribute_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 282\u001b[0m \u001b[0m_\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menum_class\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 283\u001b[0;31m \u001b[0mattr_value\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 284\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0menum_class\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 285\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0menum_class\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mattr_value\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/green.py\u001b[0m in \u001b[0;36mgreener\u001b[0;34m(obj, *args, **kwargs)\u001b[0m\n\u001b[1;32m 193\u001b[0m \u001b[0mgreen_mode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0maccess\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'green_mode'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 194\u001b[0m \u001b[0mexecutor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_object_executor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgreen_mode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 195\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mexecutor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwait\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwait\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 196\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 197\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgreener\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/green.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, fn, args, kwargs, wait, timeout)\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[0;31m# Sychronous (no delegation)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 108\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masynchronous\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0min_executor_context\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 109\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 110\u001b[0m \u001b[0;31m# Asynchronous delegation\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 111\u001b[0m \u001b[0maccessor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdelegate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__DeviceProxy__read_attribute\u001b[0;34m(self, value, extract_as)\u001b[0m\n\u001b[1;32m 439\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 440\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__DeviceProxy__read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mextract_as\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mExtractAs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumpy\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 441\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m__check_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mextract_as\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 442\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 443\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__check_read_attribute\u001b[0;34m(dev_attr)\u001b[0m\n\u001b[1;32m 155\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__check_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdev_attr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 156\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdev_attr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhas_failed\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 157\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDevFailed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mdev_attr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_err_stack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 158\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdev_attr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 159\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDevFailed\u001b[0m: DevFailed[\nDevError[\n desc = Read value for attribute RCU_mask_RW has not been updated\n origin = Device_3Impl::read_attributes_no_except\n reason = API_AttrValueNotSet\nseverity = ERR]\n\nDevError[\n desc = Failed to read_attribute on device lts/pcc/1, attribute RCU_mask_RW\n origin = DeviceProxy::read_attribute()\n reason = API_AttributeFailed\nseverity = ERR]\n]" + ] + } + ], + "source": [ + "\n", + "values = [[d.RCU_mask_RW, \"RCU_mask_RW\"],\n", + "[d.Ant_mask_RW,\"Ant_mask_RW\"],\n", + "[d.RCU_attenuator_R,\"RCU_attenuator_R\"],\n", + "[d.RCU_attenuator_RW,\"RCU_attenuator_RW\"],\n", + "[d.RCU_band_R,\"RCU_band_R\"],\n", + "[d.RCU_band_RW,\"RCU_band_RW\"],\n", + "[d.RCU_temperature_R,\"RCU_temperature_R\"],\n", + "[d.RCU_Pwr_dig_R,\"RCU_Pwr_dig_R\"],\n", + "[d.RCU_LED0_R,\"RCU_LED0_R\"],\n", + "[d.RCU_LED0_RW,\"RCU_LED0_RW\"],\n", + "[d.RCU_ADC_lock_R,\"RCU_ADC_lock_R\"],\n", + "[d.RCU_ADC_SYNC_R,\"RCU_ADC_SYNC_R\"],\n", + "[d.RCU_ADC_JESD_R,\"RCU_ADC_JESD_R\"],\n", + "[d.RCU_ADC_CML_R,\"RCU_ADC_CML_R\"],\n", + "[d.RCU_OUT1_R,\"RCU_OUT1_R\"],\n", + "[d.RCU_OUT2_R,\"RCU_OUT2_R\"],\n", + "[d.RCU_ID_R,\"RCU_ID_R\"],\n", + "[d.RCU_version_R,\"RCU_version_R\"],\n", + "[d.HBA_element_beamformer_delays_R,\"HBA_element_beamformer_delays_R\"],\n", + "[d.HBA_element_beamformer_delays_RW,\"HBA_element_beamformer_delays_RW\"],\n", + "[d.HBA_element_pwr_R,\"HBA_element_pwr_R\"],\n", + "[d.HBA_element_pwr_RW,\"HBA_element_pwr_RW\"],\n", + "[d.RCU_monitor_rate_RW,\"RCU_monitor_rate_RW\"]]\n", + "\n", + "\n", + "for i in values:\n", + " print(\"🟦🟦🟦\", i[1], \": \", i[0])\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "charitable-subject", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[False False False False False False False False False False False False\n", + " False False False False False False False False False False False False\n", + " False False False False False False False False]\n", + "current monitoring rate: 0.0, setting to 1.0\n", + "new monitoring rate is: 1.0\n" + ] + } + ], + "source": [ + "d.RCU_mask_RW = [False, False, False, False, False, False, False, False, False, False, False, False,\n", + " False, False, False, False, False, False, False, False, False, False, False, False,\n", + " False, False, False, False, False, False, False, False,]\n", + "time.sleep(1)\n", + "print(d.RCU_mask_RW)\n", + "\n", + "monitor_rate = d.RCU_monitor_rate_RW\n", + "print(\"current monitoring rate: {}, setting to {}\".format(monitor_rate, monitor_rate + 1))\n", + "d.RCU_monitor_rate_RW = monitor_rate + 1\n", + "time.sleep(2)\n", + "print(\"new monitoring rate is: {}\".format(d.RCU_monitor_rate_RW))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "impressive-request", + "metadata": {}, + "outputs": [], + "source": [ + "attr_names = d.get_attribute_list()\n", + "\n", + "for i in attr_names:\n", + " exec(\"value = print(i, d.{})\".format(i))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "conditional-scale", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "StationControl", + "language": "python", + "name": "stationcontrol" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/jupyter-notebooks/SDP_notebook.ipynb b/jupyter-notebooks/SDP_notebook.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..0845262a8dcb366d94a9ae6098d3e912569b4a27 --- /dev/null +++ b/jupyter-notebooks/SDP_notebook.ipynb @@ -0,0 +1,132 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 6, + "id": "waiting-chance", + "metadata": {}, + "outputs": [], + "source": [ + "import time" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "moving-alexandria", + "metadata": {}, + "outputs": [], + "source": [ + "d=DeviceProxy(\"LTS/SDP/1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ranking-aluminum", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "warning, expected device to be in on state, is: FAULT\n" + ] + } + ], + "source": [ + "state = str(d.state())\n", + "\n", + "if state == \"OFF\":\n", + " d.initialise()\n", + " time.sleep(1)\n", + "state = str(d.state())\n", + "if state == \"STANDBY\":\n", + " d.on()\n", + "state = str(d.state())\n", + "if state == \"ON\":\n", + " print(\"Device is now in on state\")\n", + "else:\n", + " print(\"warning, expected device to be in on state, is: \", state)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "transsexual-battle", + "metadata": {}, + "outputs": [], + "source": [ + "values = [\n", + " [d.fpga_mask_RW, \"fpga_mask_RW\"],\n", + " [d.fpga_scrap_R, \"fpga_scrap_R\"],\n", + " [d.fpga_scrap_RW, \"fpga_scrap_RW\"],\n", + " [d.fpga_status_R, \"fpga_status_R\"],\n", + " [d.fpga_temp_R, \"fpga_temp_R\"],\n", + " [d.fpga_version_R, \"fpga_version_R\"],\n", + " [d.fpga_weights_R, \"fpga_weights_R\"],\n", + " [d.fpga_weights_RW, \"fpga_weights_RW\"],\n", + " [d.tr_busy_R, \"tr_busy_R\"],\n", + " [d.tr_reload_RW, \"tr_reload_RW\"],\n", + " # [d.tr_tod_R, \"tr_tod_R\"],\n", + " # [d.tr_uptime_R, \"tr_uptime_R\"]\n", + "]\n", + "\n", + "for i in values:\n", + " print(\"🟦🟦🟦\", i[1], \": \", i[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "eligible-times", + "metadata": {}, + "outputs": [ + { + "ename": "DevFailed", + "evalue": "DevFailed[\nDevError[\n desc = TypeError: Expecting a numeric type, but it is not. If you use a numpy type instead of python core types, then it must exactly match (ex: numpy.int32 for PyTango.DevLong)\n \n origin = Traceback (most recent call last):\n File \"/usr/local/lib/python3.7/dist-packages/tango/server.py\", line 138, in read_attr\n set_complex_value(attr, ret)\n File \"/usr/local/lib/python3.7/dist-packages/tango/server.py\", line 115, in set_complex_value\n attr.set_value(value)\nTypeError: Expecting a numeric type, but it is not. If you use a numpy type instead of python core types, then it must exactly match (ex: numpy.int32 for PyTango.DevLong)\n\n reason = PyDs_PythonError\nseverity = ERR]\n\nDevError[\n desc = Failed to read_attribute on device lts/sdp/1, attribute tr_tod_R\n origin = DeviceProxy::read_attribute()\n reason = API_AttributeFailed\nseverity = ERR]\n]", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDevFailed\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-5-e44d5c52394a>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtr_tod_R\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__DeviceProxy__getattr\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 319\u001b[0m \u001b[0mattr_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__get_attr_cache\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname_l\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 320\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 321\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m__get_attribute_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 322\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 323\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mname_l\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__get_pipe_cache\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__get_attribute_value\u001b[0;34m(self, attr_info, name)\u001b[0m\n\u001b[1;32m 281\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__get_attribute_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 282\u001b[0m \u001b[0m_\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menum_class\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 283\u001b[0;31m \u001b[0mattr_value\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 284\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0menum_class\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 285\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0menum_class\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mattr_value\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/green.py\u001b[0m in \u001b[0;36mgreener\u001b[0;34m(obj, *args, **kwargs)\u001b[0m\n\u001b[1;32m 193\u001b[0m \u001b[0mgreen_mode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0maccess\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'green_mode'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 194\u001b[0m \u001b[0mexecutor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_object_executor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgreen_mode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 195\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mexecutor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwait\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwait\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 196\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 197\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgreener\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/green.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, fn, args, kwargs, wait, timeout)\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[0;31m# Sychronous (no delegation)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 108\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masynchronous\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0min_executor_context\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 109\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 110\u001b[0m \u001b[0;31m# Asynchronous delegation\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 111\u001b[0m \u001b[0maccessor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdelegate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__DeviceProxy__read_attribute\u001b[0;34m(self, value, extract_as)\u001b[0m\n\u001b[1;32m 439\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 440\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__DeviceProxy__read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mextract_as\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mExtractAs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumpy\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 441\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m__check_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mextract_as\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 442\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 443\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__check_read_attribute\u001b[0;34m(dev_attr)\u001b[0m\n\u001b[1;32m 155\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__check_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdev_attr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 156\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdev_attr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhas_failed\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 157\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDevFailed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mdev_attr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_err_stack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 158\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdev_attr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 159\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDevFailed\u001b[0m: DevFailed[\nDevError[\n desc = TypeError: Expecting a numeric type, but it is not. If you use a numpy type instead of python core types, then it must exactly match (ex: numpy.int32 for PyTango.DevLong)\n \n origin = Traceback (most recent call last):\n File \"/usr/local/lib/python3.7/dist-packages/tango/server.py\", line 138, in read_attr\n set_complex_value(attr, ret)\n File \"/usr/local/lib/python3.7/dist-packages/tango/server.py\", line 115, in set_complex_value\n attr.set_value(value)\nTypeError: Expecting a numeric type, but it is not. If you use a numpy type instead of python core types, then it must exactly match (ex: numpy.int32 for PyTango.DevLong)\n\n reason = PyDs_PythonError\nseverity = ERR]\n\nDevError[\n desc = Failed to read_attribute on device lts/sdp/1, attribute tr_tod_R\n origin = DeviceProxy::read_attribute()\n reason = API_AttributeFailed\nseverity = ERR]\n]" + ] + } + ], + "source": [ + "attr_names = d.get_attribute_list()\n", + "\n", + "for i in attr_names:\n", + " exec(\"value = print(i, d.{})\".format(i))\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "StationControl", + "language": "python", + "name": "stationcontrol" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/jupyter-notebooks/test.txt b/jupyter-notebooks/test.txt deleted file mode 100644 index 30d74d258442c7c65512eafab474568dd706c430..0000000000000000000000000000000000000000 --- a/jupyter-notebooks/test.txt +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file diff --git a/jupyter-notebooks/test_device.ipynb b/jupyter-notebooks/test_device.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..66d3a5f1057cb9051dce52325fb4fc73fb9d7005 --- /dev/null +++ b/jupyter-notebooks/test_device.ipynb @@ -0,0 +1,171 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 26, + "id": "waiting-chance", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import numpy" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "moving-alexandria", + "metadata": {}, + "outputs": [], + "source": [ + "d=DeviceProxy(\"LTS/test_device/1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "ranking-aluminum", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Device is now in on state\n" + ] + } + ], + "source": [ + "state = str(d.state())\n", + "\n", + "if state == \"OFF\":\n", + " d.initialise()\n", + " time.sleep(1)\n", + "state = str(d.state())\n", + "if state == \"STANDBY\":\n", + " d.on()\n", + "state = str(d.state())\n", + "if state == \"ON\":\n", + " print(\"Device is now in on state\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "beneficial-evidence", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "bool_scalar_R [False]\n", + "bool_scalar_RW [False]\n", + "int64_spectrum_R [0 0 0 0 0 0 0 0]\n", + "str_spectrum_RW ('', '', '', '', '', '', '', '')\n", + "double_image_R [[0. 0.]\n", + " [0. 0.]\n", + " [0. 0.]\n", + " [0. 0.]\n", + " [0. 0.]\n", + " [0. 0.]\n", + " [0. 0.]\n", + " [0. 0.]]\n", + "double_image_RW [[0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0. 0. 0. 0.]]\n", + "int32_scalar_R [0]\n", + "uint16_spectrum_RW [0 0 0 0 0 0 0 0]\n", + "float32_image_R [[0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0. 0. 0. 0.]]\n", + "uint8_image_RW [[0 0]\n", + " [0 0]\n", + " [0 0]\n", + " [0 0]\n", + " [0 0]\n", + " [0 0]\n", + " [0 0]\n", + " [0 0]]\n", + "tr_tod_R [0]\n", + "tr_uptime_R [0]\n", + "State <function __get_command_func.<locals>.f at 0x7f1c88a29e18>\n", + "Status <function __get_command_func.<locals>.f at 0x7f1c88a5abf8>\n" + ] + } + ], + "source": [ + "attr_names = d.get_attribute_list()\n", + "\n", + "for i in attr_names:\n", + " exec(\"value = print(i, d.{})\".format(i))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "sporting-current", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d.RCU_mask_RW = [False, False, False, False, False, False, False, False, False, False, False, False,\n", + " False, False, False, False, False, False, False, False, False, False, False, False,\n", + " False, False, False, False, False, False, False, False,]\n", + "time.sleep(1)\n", + "print(d.RCU_mask_RW)\n", + "\n", + "monitor_rate = d.RCU_monitor_rate_RW\n", + "print(\"current monitoring rate: {}, setting to {}\".format(monitor_rate, monitor_rate + 1))\n", + "monitor_rate = monitor_rate + 1\n", + "\n", + "time.sleep(1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sharing-mechanics", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ruled-tracy", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "StationControl", + "language": "python", + "name": "stationcontrol" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}