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
 # ----------