diff --git a/CDB/test_ConfigDb.json b/CDB/test_ConfigDb.json
index 92331cd38297e3f014321c8565b1dca69f4a7572..879d73f275d0b7c275a01219cffcea92501be870 100644
--- a/CDB/test_ConfigDb.json
+++ b/CDB/test_ConfigDb.json
@@ -19,17 +19,36 @@
                     "LTS/SDP/1": {
                         "properties": {
                             "OPC_Server_Name": [
-                                "DESPi2.astron.nl"
+                                "dop36.astron.nl"
                             ]
                         }
                     }
                 }
             }
         },
-        "example_device": {
+        "APSCTL": {
             "1": {
-                "example_device": {
-                    "LTS/example_device/1": {
+                "APSCTL": {
+                    "LTS/APSCTL/1": {
+                        "properties": {
+                            "OPC_Server_Name": [
+                                "ltspi.astron.nl"
+                            ],
+                            "OPC_Server_Port": [
+                                "4844"
+                            ],
+                            "OPC_Time_Out": [
+                                "5.0"
+                            ]
+                        }
+                    }
+                }
+            }
+        },
+        "test_device": {
+            "1": {
+                "test_device": {
+                    "LTS/test_device/1": {
                          "attribute_properties": {
                             "Ant_mask_RW": {
                                 "archive_period": [
diff --git a/CDB/windows_ConfigDb.json b/CDB/windows_ConfigDb.json
index 47c23ed110ba476e508bebe1ccf7bca1c387bd8d..c84fb3855372ba588de5bdef470d665b46ea6a99 100644
--- a/CDB/windows_ConfigDb.json
+++ b/CDB/windows_ConfigDb.json
@@ -26,10 +26,62 @@
                 }
             }
         },
-        "example_device": {
+		"ini_device": {
             "1": {
-                "example_device": {
-                    "LTS/example_device/1": {
+                "ini_device": {
+                    "LTS/ini_device/1": {
+                         "attribute_properties": {
+                            "Ant_mask_RW": {
+                                "archive_period": [
+                                    "600000"
+                                ]
+                            }
+						},
+                        "properties": {
+                            "SNMP_community": [
+                                "public"
+                            ],
+                            "SNMP_host": [
+                                "172.22.176.1"
+                            ],
+                            "SNMP_timeout": [
+                                "5.0"
+                            ]
+                        }
+                    }
+                }
+            }
+        },
+        "SNMP": {
+            "1": {
+                "SNMP": {
+                    "LTS/SNMP/1": {
+                         "attribute_properties": {
+                            "Ant_mask_RW": {
+                                "archive_period": [
+                                    "600000"
+                                ]
+                            }
+						},
+                        "properties": {
+                            "SNMP_community": [
+                                "public"
+                            ],
+                            "SNMP_host": [
+                                "172.22.176.1"
+                            ],
+                            "SNMP_timeout": [
+                                "5.0"
+                            ]
+                        }
+                    }
+                }
+            }
+        },
+		"test_device": {
+            "1": {
+                "test_device": {
+                    "LTS/test_device/1": {
                          "attribute_properties": {
                             "Ant_mask_RW": {
                                 "archive_period": [
diff --git a/devices/APSCTL.py b/devices/APSCTL.py
new file mode 100644
index 0000000000000000000000000000000000000000..e4c4dd38eaa9ab3aa18665093275749a54f3d94f
--- /dev/null
+++ b/devices/APSCTL.py
@@ -0,0 +1,187 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the SDP project
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+""" SDP Device Server for LOFAR2.0
+
+"""
+
+# PyTango imports
+from tango.server import run
+from tango.server import device_property
+from tango import AttrWriteType
+
+#attribute extention and hardware device imports
+from src.attribute_wrapper import attribute_wrapper
+from src.hardware_device import hardware_device
+import numpy
+# Additional import
+
+from clients.opcua_connection import OPCUAConnection
+
+
+__all__ = ["APSCTL", "main"]
+
+class APSCTL(hardware_device):
+    """
+
+    **Properties:**
+
+    - Device Property
+        OPC_Server_Name
+            - Type:'DevString'
+        OPC_Server_Port
+            - Type:'DevULong'
+        OPC_Time_Out
+            - Type:'DevDouble'
+    """
+
+    # -----------------
+    # Device Properties
+    # -----------------
+
+    OPC_Server_Name = device_property(
+        dtype='DevString',
+        mandatory=True
+    )
+
+    OPC_Server_Port = device_property(
+        dtype='DevULong',
+        mandatory=True
+    )
+
+    OPC_Time_Out = device_property(
+        dtype='DevDouble',
+        mandatory=True
+    )
+
+    # ----------
+    # Attributes
+    # ----------
+    N_unb = 2
+    N_fpga = 4
+    N_ddr = 2
+    N_qsfp = 6
+
+
+    # Central CP per Uniboard
+    UNB2_Power_ON_OFF_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Power_ON_OFF_RW"], datatype=numpy.bool_, dims=(N_unb,), access=AttrWriteType.READ_WRITE)
+    UNB2_Front_Panel_LED_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Front_Panel_LED_RW"], datatype=numpy.uint8, dims=(N_unb,), access=AttrWriteType.READ_WRITE)
+    UNB2_Mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Mask_RW"], datatype=numpy.bool_, dims=(N_unb,), access=AttrWriteType.READ_WRITE)
+    # Central MP per Uniboard
+    UNB2_I2C_bus_OK_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_I2C_bus_OK_R"], datatype=numpy.bool_, dims=(N_unb,))
+    UNB2_Front_Panel_LED_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Front_Panel_LED_R"], datatype=numpy.uint8, dims=(N_unb,))
+    UNB2_EEPROM_Serial_Number_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_EEPROM_Serial_Number_R"], datatype=numpy.str, dims=(N_unb,))
+    UNB2_EEPROM_Unique_ID_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_EEPROM_Unique_ID_R"], datatype=numpy.uint32, dims=(N_unb,))
+    UNB2_DC_DC_48V_12V_VIN_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_VIN_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_DC_DC_48V_12V_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_VOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_DC_DC_48V_12V_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_IOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_DC_DC_48V_12V_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_TEMP_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_QSFP_N01_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N01_VOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_QSFP_N01_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N01_IOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_QSFP_N01_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N01_TEMP_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_QSFP_N23_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N23_VOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_QSFP_N23_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N23_IOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_QSFP_N23_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N23_TEMP_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_SWITCH_1V2_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_1V2_VOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_SWITCH_1V2_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_1V2_IOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_SWITCH_1V2_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_1V2_TEMP_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_SWITCH_PHY_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_PHY_VOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_SWITCH_PHY_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_PHY_IOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_SWITCH_PHY_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_PHY_TEMP_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_CLOCK_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_CLOCK_VOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_CLOCK_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_CLOCK_IOUT_R"], datatype=numpy.double, dims=(N_unb,))
+    UNB2_POL_CLOCK_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_CLOCK_TEMP_R"], datatype=numpy.double, dims=(N_unb,))
+
+    # monitor points per FPGA
+    UNB2_FPGA_DDR4_SLOT_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_DDR4_SLOT_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_DDR4_SLOT_PART_NUMBER_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_DDR4_SLOT_PART_NUMBER_R"], datatype=numpy.str, dims=(N_unb * N_qsfp,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_0_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_0_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_1_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_1_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_2_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_2_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_3_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_3_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_4_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_4_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_5_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_5_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_0_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_0_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_1_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_1_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_2_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_2_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_3_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_3_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_4_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_4_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga))
+    UNB2_FPGA_QSFP_CAGE_5_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_5_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_CORE_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_CORE_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_CORE_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_ERAM_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_ERAM_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_ERAM_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_RXGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_RXGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_RXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_TXGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_TXGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_TXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_HGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_HGXB_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_HGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_HGXB_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_HGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_HGXB_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_PGM_VOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_PGM_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_PGM_IOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_PGM_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+    UNB2_FPGA_POL_PGM_TEMP_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_PGM_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga))
+
+
+    def delete_device(self):
+        """Hook to delete resources allocated in init_device.
+
+        This method allows for any memory or other resources allocated in the
+        init_device method to be released.  This method is called by the device
+        destructor and by the device Init command (a Tango built-in).
+        """
+        self.debug_stream("Shutting down...")
+
+        self.Off()
+        self.debug_stream("Shut down.  Good bye.")
+
+    # --------
+    # overloaded functions
+    # --------
+    def off(self):
+        """ user code here. is called when the state is set to OFF """
+
+        # Stop keep-alive
+        self.opcua_connection.stop()
+
+    def initialise(self):
+        """ user code here. is called when the sate is set to INIT """
+        """Initialises the attributes and properties of the PCC."""
+
+        # set up the OPC ua client
+        self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Fault, self)
+
+        # map an access helper class
+        for i in self.attr_list():
+            try:
+                i.set_comm_client(self.OPCua_client)
+            except:
+                self.debug_stream("error in getting APSCTL attribute: {} from client".format(i))
+
+        self.OPCua_client.start()
+
+    # --------
+    # Commands
+    # --------
+
+# ----------
+# Run server
+# ----------
+def main(args=None, **kwargs):
+    """Main function of the SDP module."""
+    return run((APSCTL,), args=args, **kwargs)
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/devices/HW_device_template.py b/devices/HW_device_template.py
index c9365b496f7717d764b4f38d1011ece09a8d1665..cd7880672caf41e65db841408353896a795c5549 100644
--- a/devices/HW_device_template.py
+++ b/devices/HW_device_template.py
@@ -1,25 +1,25 @@
 # -*- coding: utf-8 -*-
 #
-# This file is part of the PCC project
-#
-#
+# This file wraps around a tango device class and provides a number of abstractions useful for hardware devices. It works together
 #
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-""" Hardware Device Server for LOFAR2.0
+"""
 
 """
 
 # PyTango imports
 from tango.server import run
+from tango import AttrWriteType
 # Additional import
 
-from src.hardware_device import *
-
+from src.attribute_wrapper import attribute_wrapper
+from src.hardware_device import hardware_device
 
 __all__ = ["HW_dev"]
 
+
 class HW_dev(hardware_device):
     """
     This class is the minimal (read empty) implementation of a class using 'hardware_device'
@@ -29,7 +29,7 @@ class HW_dev(hardware_device):
     # Attributes
     # ----------
     """
-    attribute wrapper objects can be declared here. All attribute wrapper objects will get automatically put in a ist (attr_list) for easy access
+    attribute wrapper objects can be declared here. All attribute wrapper objects will get automatically put in a list (attr_list) for easy access
 
     example = attribute_wrapper(comms_annotation="this is an example", datatype=numpy.double, dims=(8, 2), access=AttrWriteType.READ_WRITE)
     ...
@@ -76,6 +76,7 @@ class HW_dev(hardware_device):
         """ user code here. is called when the sate is set to INIT """
         pass
 
+
 # ----------
 # Run server
 # ----------
@@ -86,4 +87,3 @@ def main(args=None, **kwargs):
 
 if __name__ == '__main__':
     main()
-
diff --git a/devices/LICENSE.txt b/devices/LICENSE.txt
index c9978b8eee263aebfdd8d0a016e447940682ba8b..8a0eaeb196094a651006f51fd99c0c05cb16ccd6 100644
--- a/devices/LICENSE.txt
+++ b/devices/LICENSE.txt
@@ -1,201 +1,201 @@
-                              Apache License
-                        Version 2.0, January 2004
-                     http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
-   "License" shall mean the terms and conditions for use, reproduction,
-   and distribution as defined by Sections 1 through 9 of this document.
-
-   "Licensor" shall mean the copyright owner or entity authorized by
-   the copyright owner that is granting the License.
-
-   "Legal Entity" shall mean the union of the acting entity and all
-   other entities that control, are controlled by, or are under common
-   control with that entity. For the purposes of this definition,
-   "control" means (i) the power, direct or indirect, to cause the
-   direction or management of such entity, whether by contract or
-   otherwise, or (ii) ownership of fifty percent (50%) or more of the
-   outstanding shares, or (iii) beneficial ownership of such entity.
-
-   "You" (or "Your") shall mean an individual or Legal Entity
-   exercising permissions granted by this License.
-
-   "Source" form shall mean the preferred form for making modifications,
-   including but not limited to software source code, documentation
-   source, and configuration files.
-
-   "Object" form shall mean any form resulting from mechanical
-   transformation or translation of a Source form, including but
-   not limited to compiled object code, generated documentation,
-   and conversions to other media types.
-
-   "Work" shall mean the work of authorship, whether in Source or
-   Object form, made available under the License, as indicated by a
-   copyright notice that is included in or attached to the work
-   (an example is provided in the Appendix below).
-
-   "Derivative Works" shall mean any work, whether in Source or Object
-   form, that is based on (or derived from) the Work and for which the
-   editorial revisions, annotations, elaborations, or other modifications
-   represent, as a whole, an original work of authorship. For the purposes
-   of this License, Derivative Works shall not include works that remain
-   separable from, or merely link (or bind by name) to the interfaces of,
-   the Work and Derivative Works thereof.
-
-   "Contribution" shall mean any work of authorship, including
-   the original version of the Work and any modifications or additions
-   to that Work or Derivative Works thereof, that is intentionally
-   submitted to Licensor for inclusion in the Work by the copyright owner
-   or by an individual or Legal Entity authorized to submit on behalf of
-   the copyright owner. For the purposes of this definition, "submitted"
-   means any form of electronic, verbal, or written communication sent
-   to the Licensor or its representatives, including but not limited to
-   communication on electronic mailing lists, source code control systems,
-   and issue tracking systems that are managed by, or on behalf of, the
-   Licensor for the purpose of discussing and improving the Work, but
-   excluding communication that is conspicuously marked or otherwise
-   designated in writing by the copyright owner as "Not a Contribution."
-
-   "Contributor" shall mean Licensor and any individual or Legal Entity
-   on behalf of whom a Contribution has been received by Licensor and
-   subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of
-   this License, each Contributor hereby grants to You a perpetual,
-   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-   copyright license to reproduce, prepare Derivative Works of,
-   publicly display, publicly perform, sublicense, and distribute the
-   Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of
-   this License, each Contributor hereby grants to You a perpetual,
-   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-   (except as stated in this section) patent license to make, have made,
-   use, offer to sell, sell, import, and otherwise transfer the Work,
-   where such license applies only to those patent claims licensable
-   by such Contributor that are necessarily infringed by their
-   Contribution(s) alone or by combination of their Contribution(s)
-   with the Work to which such Contribution(s) was submitted. If You
-   institute patent litigation against any entity (including a
-   cross-claim or counterclaim in a lawsuit) alleging that the Work
-   or a Contribution incorporated within the Work constitutes direct
-   or contributory patent infringement, then any patent licenses
-   granted to You under this License for that Work shall terminate
-   as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the
-   Work or Derivative Works thereof in any medium, with or without
-   modifications, and in Source or Object form, provided that You
-   meet the following conditions:
-
-   (a) You must give any other recipients of the Work or
-       Derivative Works a copy of this License; and
-
-   (b) You must cause any modified files to carry prominent notices
-       stating that You changed the files; and
-
-   (c) You must retain, in the Source form of any Derivative Works
-       that You distribute, all copyright, patent, trademark, and
-       attribution notices from the Source form of the Work,
-       excluding those notices that do not pertain to any part of
-       the Derivative Works; and
-
-   (d) If the Work includes a "NOTICE" text file as part of its
-       distribution, then any Derivative Works that You distribute must
-       include a readable copy of the attribution notices contained
-       within such NOTICE file, excluding those notices that do not
-       pertain to any part of the Derivative Works, in at least one
-       of the following places: within a NOTICE text file distributed
-       as part of the Derivative Works; within the Source form or
-       documentation, if provided along with the Derivative Works; or,
-       within a display generated by the Derivative Works, if and
-       wherever such third-party notices normally appear. The contents
-       of the NOTICE file are for informational purposes only and
-       do not modify the License. You may add Your own attribution
-       notices within Derivative Works that You distribute, alongside
-       or as an addendum to the NOTICE text from the Work, provided
-       that such additional attribution notices cannot be construed
-       as modifying the License.
-
-   You may add Your own copyright statement to Your modifications and
-   may provide additional or different license terms and conditions
-   for use, reproduction, or distribution of Your modifications, or
-   for any such Derivative Works as a whole, provided Your use,
-   reproduction, and distribution of the Work otherwise complies with
-   the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise,
-   any Contribution intentionally submitted for inclusion in the Work
-   by You to the Licensor shall be under the terms and conditions of
-   this License, without any additional terms or conditions.
-   Notwithstanding the above, nothing herein shall supersede or modify
-   the terms of any separate license agreement you may have executed
-   with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade
-   names, trademarks, service marks, or product names of the Licensor,
-   except as required for reasonable and customary use in describing the
-   origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or
-   agreed to in writing, Licensor provides the Work (and each
-   Contributor provides its Contributions) on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-   implied, including, without limitation, any warranties or conditions
-   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-   PARTICULAR PURPOSE. You are solely responsible for determining the
-   appropriateness of using or redistributing the Work and assume any
-   risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory,
-   whether in tort (including negligence), contract, or otherwise,
-   unless required by applicable law (such as deliberate and grossly
-   negligent acts) or agreed to in writing, shall any Contributor be
-   liable to You for damages, including any direct, indirect, special,
-   incidental, or consequential damages of any character arising as a
-   result of this License or out of the use or inability to use the
-   Work (including but not limited to damages for loss of goodwill,
-   work stoppage, computer failure or malfunction, or any and all
-   other commercial damages or losses), even if such Contributor
-   has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing
-   the Work or Derivative Works thereof, You may choose to offer,
-   and charge a fee for, acceptance of support, warranty, indemnity,
-   or other liability obligations and/or rights consistent with this
-   License. However, in accepting such obligations, You may act only
-   on Your own behalf and on Your sole responsibility, not on behalf
-   of any other Contributor, and only if You agree to indemnify,
-   defend, and hold each Contributor harmless for any liability
-   incurred by, or claims asserted against, such Contributor by reason
-   of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
-   To apply the Apache License to your work, attach the following
-   boilerplate notice, with the fields enclosed by brackets "[]"
-   replaced with your own identifying information. (Don't include
-   the brackets!)  The text should be enclosed in the appropriate
-   comment syntax for the file format. We also recommend that a
-   file or class name and description of purpose be included on the
-   same "printed page" as the copyright notice for easier
-   identification within third-party archives.
-
-Copyright 2021 ASTRON Netherlands Institute for Radio Astronomy
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright 2021 ASTRON Netherlands Institute for Radio Astronomy
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/devices/PCC.py b/devices/PCC.py
index 753b06285329bc50a980f795839c6f48056c73ef..96ef1013fa310d517fbf4637d1c423e08c10db15 100644
--- a/devices/PCC.py
+++ b/devices/PCC.py
@@ -15,21 +15,38 @@
 from tango import DebugIt
 from tango.server import run, command
 from tango.server import device_property
-
+from tango import AttrWriteType
+import numpy
 # Additional import
+
+from src.wrappers import *
+
 from clients.opcua_connection import OPCUAConnection
-from src.attribute_wrapper import *
-from src.hardware_device import *
+from src.attribute_wrapper import attribute_wrapper
+from src.hardware_device import hardware_device
 from src.lofar_logging import device_logging_to_python
 
-
 __all__ = ["PCC", "main"]
 
 @device_logging_to_python({"device": "PCC"})
 class PCC(hardware_device):
+    """
+
+    **Properties:**
+
+    - Device Property
+        OPC_Server_Name
+            - Type:'DevString'
+        OPC_Server_Port
+            - Type:'DevULong'
+        OPC_Time_Out
+            - Type:'DevDouble'
+    """
+
     # -----------------
     # Device Properties
     # -----------------
+
     OPC_Server_Name = device_property(
         dtype='DevString',
         mandatory=True
@@ -44,8 +61,7 @@ class PCC(hardware_device):
         dtype='DevDouble',
         mandatory=True
     )
-
-    OPC_Namespace = device_property(
+    OPC_namespace = device_property(
         dtype='DevString',
         mandatory=False
     )
@@ -53,56 +69,36 @@ class PCC(hardware_device):
     # ----------
     # Attributes
     # ----------
-    RCU_state_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_state_R"], datatype=numpy.str_, access=AttrWriteType.READ_WRITE)
-
     RCU_mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_mask_RW"], datatype=numpy.bool_, dims=(32,), access=AttrWriteType.READ_WRITE)
-
     Ant_mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:Ant_mask_RW"], datatype=numpy.bool_, dims=(3, 32), access=AttrWriteType.READ_WRITE)
-
     RCU_attenuator_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_attenuator_R"], datatype=numpy.int64, dims=(3, 32))
-
-    RCU_attenuator_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_attenuator_RW"], datatype=numpy.int64, dims=(3, 32), access=AttrWriteType.READ_WRITE)
-
+    RCU_attenuator_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_attenuator_RW"], datatype=numpy.int64, dims=(3, 32),
+                                          access=AttrWriteType.READ_WRITE)
     RCU_band_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_band_R"], datatype=numpy.int64, dims=(3, 32))
-
     RCU_band_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_band_RW"], datatype=numpy.int64, dims=(3, 32), access=AttrWriteType.READ_WRITE)
-
     RCU_temperature_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_temperature_R"], datatype=numpy.float64, dims=(32,))
-
     RCU_Pwr_dig_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_Pwr_dig_R"], datatype=numpy.int64, dims=(32,))
-
     RCU_LED0_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_LED0_R"], datatype=numpy.int64, dims=(32,))
-
     RCU_LED0_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_LED0_RW"], datatype=numpy.int64, dims=(32,), access=AttrWriteType.READ_WRITE)
-
     RCU_ADC_lock_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_ADC_lock_R"], datatype=numpy.int64, dims=(3, 32))
-
     RCU_ADC_SYNC_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_ADC_SYNC_R"], datatype=numpy.int64, dims=(3, 32))
-
     RCU_ADC_JESD_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_ADC_JESD_R"], datatype=numpy.int64, dims=(3, 32))
-
     RCU_ADC_CML_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_ADC_CML_R"], datatype=numpy.int64, dims=(3, 32))
-
     RCU_OUT1_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_OUT1_R"], datatype=numpy.int64, dims=(3, 32))
-
     RCU_OUT2_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_OUT2_R"], datatype=numpy.int64, dims=(3, 32))
-
     RCU_ID_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_ID_R"], datatype=numpy.int64, dims=(32,))
-
     RCU_version_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_version_R"], datatype=numpy.str_, dims=(32,))
 
-    HBA_element_beamformer_delays_R = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_beamformer_delays_R"], datatype=numpy.int64, dims=(32, 96))
-
-    HBA_element_beamformer_delays_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_beamformer_delays_RW"], datatype=numpy.int64, dims=(32, 96), access=AttrWriteType.READ_WRITE)
-
+    HBA_element_beamformer_delays_R = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_beamformer_delays_R"], datatype=numpy.int64,
+                                                        dims=(32, 96))
+    HBA_element_beamformer_delays_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_beamformer_delays_RW"], datatype=numpy.int64,
+                                                         dims=(32, 96), access=AttrWriteType.READ_WRITE)
     HBA_element_pwr_R = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_pwr_R"], datatype=numpy.int64, dims=(32, 96))
+    HBA_element_pwr_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_pwr_RW"], datatype=numpy.int64, dims=(32, 96),
+                                           access=AttrWriteType.READ_WRITE)
 
-    HBA_element_pwr_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:HBA_element_pwr_RW"], datatype=numpy.int64, dims=(32, 96), access=AttrWriteType.READ_WRITE)
-
-    uC_ID_R = attribute_wrapper(comms_annotation=["2:PCC", "2:uC_ID_R"], datatype=numpy.int64, dims=(32,))
-
-    RCU_monitor_rate_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_monitor_rate_RW"], datatype=numpy.float64, access=AttrWriteType.READ_WRITE)
-
+    RCU_monitor_rate_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_monitor_rate_RW"], datatype=numpy.float64,
+                                            access=AttrWriteType.READ_WRITE)
 
     def delete_device(self):
         """Hook to delete resources allocated in init_device.
@@ -112,6 +108,7 @@ class PCC(hardware_device):
         destructor and by the device Init command (a Tango built-in).
         """
         self.debug_stream("Shutting down...")
+
         self.Off()
         self.debug_stream("Shut down.  Good bye.")
 
@@ -120,19 +117,25 @@ class PCC(hardware_device):
     # --------
     def off(self):
         """ user code here. is called when the state is set to OFF """
-
         # Stop keep-alive
-        self.OPCua_client.disconnect()
+        self.OPCua_client.stop()
 
     def initialise(self):
         """ user code here. is called when the state is set to INIT """
 
-        #set up the OPC ua client
-        namespace = "http://lofar.eu"
-        if self.OPC_Namespace is not None:
-            namespace = self.OPC_Namespace
-
-        self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), namespace, self.OPC_Time_Out, self.Standby, self.Fault, self)
+        # Init the dict that contains function to OPC-UA function mappings.
+        self.function_mapping = {}
+        self.function_mapping["RCU_on"] = {}
+        self.function_mapping["RCU_off"] = {}
+        self.function_mapping["ADC_on"] = {}
+        self.function_mapping["RCU_update"] = {}
+        self.function_mapping["CLK_on"] = {}
+        self.function_mapping["CLK_off"] = {}
+        self.function_mapping["CLK_PLL_setup"] = {}
+
+        # set up the OPC ua client
+        self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu",
+                                            self.OPC_Time_Out, self.Fault, self)
 
         # map the attributes to the OPC ua comm client
         for i in self.attr_list():
@@ -141,16 +144,6 @@ class PCC(hardware_device):
             except:
                 pass
 
-        # Init the dict that contains function to OPC-UA function mappings.
-        self.function_mapping = {}
-        self.function_mapping["RCU_off"] = self.OPCua_client._setup_annotation(["2:PCC", "2:RCU_off"])
-        self.function_mapping["RCU_on"] = self.OPCua_client._setup_annotation(["2:PCC", "2:RCU_on"])
-        self.function_mapping["ADC_on"] = self.OPCua_client._setup_annotation(["2:PCC", "2:ADC_on"])
-        self.function_mapping["RCU_update"] = self.OPCua_client._setup_annotation(["2:PCC", "2:RCU_update"])
-        self.function_mapping["CLK_off"] = self.OPCua_client._setup_annotation(["2:PCC", "2:CLK_off"])
-        self.function_mapping["CLK_on"] = self.OPCua_client._setup_annotation(["2:PCC", "2:CLK_on"])
-        self.function_mapping["CLK_PLL_setup"] = self.OPCua_client._setup_annotation(["2:PCC", "2:CLK_PLL_setup"])
-
         self.OPCua_client.start()
 
     # --------
@@ -244,4 +237,3 @@ def main(args=None, **kwargs):
 
 if __name__ == '__main__':
     main()
-
diff --git a/devices/SDP.py b/devices/SDP.py
index 10a1d8a5578b0df80cad13ef2dd2b9e9482d51c0..35c03232e8e0fb42b272f25325f20cad9ba03340 100644
--- a/devices/SDP.py
+++ b/devices/SDP.py
@@ -14,13 +14,16 @@
 # PyTango imports
 from tango.server import run
 from tango.server import device_property
+from tango import AttrWriteType
 # Additional import
 
 from clients.opcua_connection import OPCUAConnection
-from src.attribute_wrapper import *
-from src.hardware_device import *
+from src.attribute_wrapper import attribute_wrapper
+from src.hardware_device import hardware_device
+
 from src.lofar_logging import device_logging_to_python
 
+import numpy
 
 __all__ = ["SDP", "main"]
 
@@ -105,10 +108,7 @@ class SDP(hardware_device):
         """Initialises the attributes and properties of the PCC."""
 
         # set up the OPC ua client
-        self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Standby, self.Fault, self)
-
-        # will contain all the values for this object
-        self.setup_value_dict()
+        self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Fault, self)
 
         # map an access helper class
         for i in self.attr_list():
diff --git a/devices/clients/opcua_connection.py b/devices/clients/opcua_connection.py
index 676b24c650f0de251d850625fce1a47b1bc6518a..85962f4216013af69733eeab145ce2d39e460a5b 100644
--- a/devices/clients/opcua_connection.py
+++ b/devices/clients/opcua_connection.py
@@ -1,5 +1,9 @@
-from src.comms_client import *
+from threading import Thread
+import socket
+from src.comms_client import CommClient
+import numpy
 import opcua
+from opcua import Client
 
 __all__ = ["OPCUAConnection"]
 
@@ -33,13 +37,13 @@ class OPCUAConnection(CommClient):
     def start(self):
         super().start()
 
-    def __init__(self, address, namespace, timeout, on_func, fault_func, streams, try_interval=2):
+    def __init__(self, address, namespace, timeout, fault_func, streams, try_interval=2):
         """
         Create the OPC ua client and connect() to it and get the object node
         """
-        super().__init__(on_func, fault_func, streams, try_interval)
+        super().__init__(fault_func, streams, try_interval)
 
-        self.client = opcua.Client(address, timeout)
+        self.client = Client(address, timeout)
 
         # Explicitly connect
         if not self.connect():
@@ -77,7 +81,7 @@ class OPCUAConnection(CommClient):
             return True
         except socket.error as e:
             self.streams.error_stream("Could not connect to server %s: %s", self._servername(), e)
-            raise Exception("") from e
+            raise Exception("Could not connect to server %s", self._servername()) from e
 
 
     def disconnect(self):
@@ -89,19 +93,16 @@ class OPCUAConnection(CommClient):
         try:
             self.client.disconnect()
         except Exception as e:
-            self.streams.error_stream("Disconnect from OPC-UA server {} failed: {}".format(self._servername(), e))
+            self.streams.error_stream("Disconnect from OPC-UA server %s failed: %s", self._servername(), e)
 
     def ping(self):
         """
         ping the client to make sure the connection with the client is still functional.
         """
         try:
-            if self.connected is True:
-                self.client.send_hello()
-            else:
-                self.streams.debug_stream("Will not ping OPC-UA server {} because the connection is inactive.".format(self._servername()))
+            self.client.send_hello()
         except Exception as e:
-            raise Exception("Lost connection to server {}.".format(self._servername())) from e
+            raise Exception("Lost connection to server %s: %s", self._servername(), e)
 
     def _setup_annotation(self, annotation):
         """
@@ -117,12 +118,13 @@ class OPCUAConnection(CommClient):
         elif isinstance(annotation, list):
             path = annotation
         else:
-            raise Exception("OPC-ua mapping requires either a list of the path or dict with the path. Was given {} type containing: {}".format(type(annotation), annotation))
+            raise Exception("OPC-ua mapping requires either a list of the path or dict with the path. Was given %s type containing: %s", type(annotation), annotation)
 
         try:
             node = self.obj.get_child(path)
         except Exception as e:
-            raise Exception("Could not get node: {} on server {}".format(path, self._servername())) from e
+            self.streams.error_stream("Could not get node: %s on server %s: %s", path, self._servername(), e)
+            raise Exception("Could not get node: %s on server %s", path, self._servername()) from e
 
         return node
 
@@ -134,7 +136,7 @@ class OPCUAConnection(CommClient):
 
         dim_x = attribute.dim_x
         dim_y = attribute.dim_y
-        ua_type = numpy_to_OPCua_dict[attribute.numpy_type]  # convert the numpy type to a corresponding UA type
+        ua_type = numpy_to_OPCua_dict[attribute.numpy_type]	 # convert the numpy type to a corresponding UA type
 
         return dim_x, dim_y, ua_type
 
diff --git a/devices/clients/test_client.py b/devices/clients/test_client.py
index 662bd692b462953414208c4cb1fc346474a2827b..b191d991ff85d3a201415224b0ee33572fcbd188 100644
--- a/devices/clients/test_client.py
+++ b/devices/clients/test_client.py
@@ -1,8 +1,11 @@
-from src.comms_client import *
+from src.comms_client import CommClient
+import numpy
+
+import os
 
 # <class 'numpy.bool_'>
 
-class example_client(CommClient):
+class test_client(CommClient):
     """
     this class provides an example implementation of a comms_client.
     Durirng initialisation it creates a correctly shaped zero filled value. on read that value is returned and on write its modified.
@@ -11,11 +14,11 @@ class example_client(CommClient):
     def start(self):
         super().start()
 
-    def __init__(self, standby_func, fault_func, streams, try_interval=2):
+    def __init__(self, fault_func, streams, try_interval=2):
         """
         initialises the class and tries to connect to the client.
         """
-        super().__init__(standby_func, fault_func, streams, try_interval)
+        super().__init__(fault_func, streams, try_interval)
 
         # Explicitly connect
         if not self.connect():
@@ -27,11 +30,10 @@ class example_client(CommClient):
         """
         this function provides a location for the code neccecary to connect to the client
         """
-
         self.streams.debug_stream("the example client doesn't actually connect to anything silly")
 
-        self.connected = True    # set connected to true
-        return True        # if succesfull, return true. otherwise return false
+        self.connected = True  # set connected to true
+        return True  # if succesfull, return true. otherwise return false
 
     def disconnect(self):
         self.connected = False  # always force a reconnect, regardless of a successful disconnect
@@ -45,7 +47,9 @@ class example_client(CommClient):
         the annotation can be in whatever format may be required. it is up to the user to handle its content
         example annotation may include:
         - a file path and file line/location
-        - COM object path
+        - server address
+        - IDs
+        - data structures
         """
 
         # as this is an example, just print the annotation
@@ -66,7 +70,6 @@ class example_client(CommClient):
 
         return dims, dtype
 
-
     def _setup_mapping(self, dims, dtype):
         """
         takes all gathered data to configure and return the correct read and write functions
@@ -85,7 +88,6 @@ class example_client(CommClient):
         self.streams.debug_stream("created and bound example_client read/write functions to attribute_wrapper object")
         return read_function, write_function
 
-
     def setup_attribute(self, annotation=None, attribute=None):
         """
         MANDATORY function: is used by the attribute wrapper to get read/write functions.
@@ -103,4 +105,3 @@ class example_client(CommClient):
 
         # return the read/write functions
         return read_function, write_function
-
diff --git a/devices/src/attribute_wrapper.py b/devices/src/attribute_wrapper.py
index bdf457df41ba76c037de64faf4e99ecf4940ca70..98faf037c363fa92006bc656f821d636b5b396c0 100644
--- a/devices/src/attribute_wrapper.py
+++ b/devices/src/attribute_wrapper.py
@@ -5,28 +5,32 @@ import numpy
 
 from src.wrappers import only_when_on, fault_on_error
 import logging
+
 logger = logging.getLogger()
 
 
 class attribute_wrapper(attribute):
     """
-        Wraps all the attributes in a wrapper class to manage most of the redundant code behind the scenes
+    Wraps all the attributes in a wrapper class to manage most of the redundant code behind the scenes
     """
 
     def __init__(self, comms_annotation=None, datatype=None, dims=(1,), access=AttrWriteType.READ, init_value=None, **kwargs):
         """
         wraps around the tango Attribute class. Provides an easier interface for 1d or 2d arrays. Also provides a way to abstract
         managing the communications interface.
+
+        comms_annotation: data passed along to the attribute. can be given any form of data. handling is up to client implementation
+        datatype: any numpy datatype
+        dims: dimensions of the
+        init_value: value
         """
 
         # ensure the type is a numpy array
-        if "numpy" not in str(datatype) and type(datatype) != str:
+        if "numpy" not in str(datatype) and datatype != str:
             raise TypeError("Attribute needs to be a Tango-supported numpy or str type, but has type \"%s\"" % (datatype,))
 
-
-
-        self.comms_annotation = comms_annotation # store data that can be used by the comms interface. not used by the wrapper itself
-        self.numpy_type = datatype # tango changes our attribute to their representation (E.g numpy.int64 becomes "DevLong64")
+        self.comms_annotation = comms_annotation  # store data that can be used by the comms interface. not used by the wrapper itself
+        self.numpy_type = datatype  # tango changes our attribute to their representation (E.g numpy.int64 becomes "DevLong64")
 
         self.init_value = init_value
         max_dim_y = 0
@@ -54,7 +58,6 @@ class attribute_wrapper(attribute):
             # scalar, just set the single dimension
             max_dim_x = 1
 
-
         if access == AttrWriteType.READ_WRITE:
             """ if the attribute is of READ_WRITE type, assign the RW and write function to it"""
 
@@ -68,8 +71,8 @@ class attribute_wrapper(attribute):
                 try:
                     return device.value_dict[self]
                 except Exception as e:
-                    raise Exception("Attribute read_RW function error, attempted to read value_dict with key: `%s`, are you sure this exists?", self) from e
-
+                    raise Exception("Attribute read_RW function error, attempted to read value_dict with key: `%s`, are you sure this exists?",
+                                    self) from e
 
             @only_when_on
             @fault_on_error
@@ -77,6 +80,7 @@ class attribute_wrapper(attribute):
                 """
                 _write_RW writes a value to this attribute
                 """
+
                 self.write_function(value)
                 device.value_dict[self] = value
 
@@ -133,7 +137,8 @@ class attribute_wrapper(attribute):
         except Exception as e:
             def pass_func(value=None):
                 pass
-            logger.error("setting comm_client failed. using pass function instead")
+
+            logger.error("Exception while setting %s attribute with annotation: '%s' read/write functions. using pass function instead to to keep running", client.__class__.__name__, self.comms_annotation)
 
             self.read_function = pass_func
             self.write_function = pass_func
diff --git a/devices/src/comms_client.py b/devices/src/comms_client.py
index 6454ddffb14a5ab041ab8a3045ccf97e0226b0df..f189ce3fdec74bad5c40c2186bfc6b3f3c207cca 100644
--- a/devices/src/comms_client.py
+++ b/devices/src/comms_client.py
@@ -1,21 +1,15 @@
 from threading import Thread
-import socket
 import time
-import numpy
-
-from tango import DevState
-
 
 class CommClient(Thread):
     """
     The ProtocolHandler class is the generic interface class between the tango attribute_wrapper and the outside world
     """
 
-    def __init__(self, standby_func, fault_func, streams, try_interval=2):
+    def __init__(self, fault_func, streams, try_interval=2):
         """
 
         """
-        self.standby_func = standby_func
         self.fault_func = fault_func
         self.try_interval = try_interval
         self.streams = streams
@@ -45,14 +39,12 @@ class CommClient(Thread):
             self.fault_func()
             return
 
-        self.standby_func()
-
         self.stopping = False
         while not self.stopping:
             # keep trying to connect
             if not self.connected:
                 if self.connect():
-                    self.standby_func()
+                    pass
                 else:
                     # we retry only once, to catch exotic network issues. if the infra or hardware is down,
                     # our device cannot help, and must be reinitialised after the infra or hardware is fixed.
@@ -74,7 +66,7 @@ class CommClient(Thread):
                 self.fault_func()
 
     def ping(self):
-        pass
+        return
 
     def stop(self):
         """
diff --git a/devices/src/hardware_device.py b/devices/src/hardware_device.py
index a65f91287027608057657254693c01c2765270bc..431cbe9718fb3b586eea9a23aaa65aa1134fa59d 100644
--- a/devices/src/hardware_device.py
+++ b/devices/src/hardware_device.py
@@ -16,15 +16,14 @@ from tango.server import Device, command
 from tango import DevState, DebugIt
 # Additional import
 
-from src.attribute_wrapper import *
+from src.attribute_wrapper import attribute_wrapper
 from src.lofar_logging import log_exceptions
 
-
 __all__ = ["hardware_device"]
 
 from src.wrappers import only_in_states
 
-
+#@log_exceptions()
 class hardware_device(Device):
     """
 
@@ -60,7 +59,6 @@ class hardware_device(Device):
 
         self.value_dict = {i: i.initial_value() for i in self.attr_list()}
 
-    @log_exceptions()
     def init_device(self):
         """ Instantiates the device in the OFF state. """
 
@@ -84,19 +82,9 @@ class hardware_device(Device):
         """
         self.set_state(DevState.INIT)
         self.setup_value_dict()
-        self.initialise()
-        self.standby()
-        self.set_state(DevState.STANDBY)
 
-    @only_in_states([DevState.INIT])
-    def Standby(self):
-        """
-        Command to ask for initialisation of this device. Can only be called in FAULT or OFF state.
-
-        :return:None
-        """
+        self.initialise()
 
-        self.standby()
         self.set_state(DevState.STANDBY)
 
     @command()
@@ -155,8 +143,6 @@ class hardware_device(Device):
         pass
     def on(self):
         pass
-    def standby(self):
-        pass
     def initialise(self):
         pass
 
@@ -164,7 +150,6 @@ class hardware_device(Device):
         """Method always executed before any TANGO command is executed."""
         pass
 
-    @log_exceptions()
     def delete_device(self):
         """Hook to delete resources allocated in init_device.
 
diff --git a/devices/test_device.py b/devices/test_device.py
index ea7c333512b41d0dcb25efd1b1cbb556010f1881..38f688cc0cb715667b8237c4358f32024ced582a 100644
--- a/devices/test_device.py
+++ b/devices/test_device.py
@@ -7,8 +7,7 @@
 # Distributed under the terms of the APACHE license.
 # See LICENSE.txt for more info.
 
-""" PCC Device Server for LOFAR2.0
-
+""" test Device Server
 """
 
 # PyTango imports
@@ -17,7 +16,7 @@ from tango.server import device_property
 from tango import DevState
 # Additional import
 
-from clients.test_client import example_client
+from clients.test_client import test_client
 from src.attribute_wrapper import *
 from src.hardware_device import *
 
@@ -25,7 +24,6 @@ __all__ = ["test_device", "main"]
 
 
 class test_device(hardware_device):
-
     # -----------------
     # Device Properties
     # -----------------
@@ -48,16 +46,20 @@ class test_device(hardware_device):
     bool_scalar_R = attribute_wrapper(comms_annotation="numpy.bool_ type read scalar", datatype=numpy.bool_)
     bool_scalar_RW = attribute_wrapper(comms_annotation="numpy.bool_ type read/write scalar", datatype=numpy.bool_, access=AttrWriteType.READ_WRITE)
 
-    int64_spectrum_R = attribute_wrapper(comms_annotation="numpy.int64 type read spectrum (len = 8)", datatype=numpy.int64, dims=(8,))
-    str_spectrum_RW = attribute_wrapper(comms_annotation="numpy.str type read/write spectrum (len = 8)", datatype=numpy.str_, dims=(8,), access=AttrWriteType.READ_WRITE)
+    int32_spectrum_R = attribute_wrapper(comms_annotation="numpy.int32 type read spectrum (len = 8)", datatype=numpy.int32, dims=(8,))
+    int32_spectrum_RW = attribute_wrapper(comms_annotation="numpy.int32 type read spectrum (len = 8)", datatype=numpy.int32, dims=(8,),
+                                          access=AttrWriteType.READ_WRITE)
 
     double_image_R = attribute_wrapper(comms_annotation="numpy.double type read image (dims = 2x8)", datatype=numpy.double, dims=(2, 8))
-    double_image_RW = attribute_wrapper(comms_annotation="numpy.double type read/write image (dims = 8x2)", datatype=numpy.double, dims=(8, 2), access=AttrWriteType.READ_WRITE)
+    double_image_RW = attribute_wrapper(comms_annotation="numpy.double type read/write image (dims = 8x2)", datatype=numpy.double, dims=(8, 2),
+                                        access=AttrWriteType.READ_WRITE)
 
     int32_scalar_R = attribute_wrapper(comms_annotation="numpy.int32 type read scalar", datatype=numpy.int32)
-    uint16_spectrum_RW = attribute_wrapper(comms_annotation="numpy.uint16 type read/write spectrum (len = 8)", datatype=numpy.uint16, dims=(8,), access=AttrWriteType.READ_WRITE)
+    uint16_spectrum_RW = attribute_wrapper(comms_annotation="numpy.uint16 type read/write spectrum (len = 8)", datatype=numpy.uint16, dims=(8,),
+                                           access=AttrWriteType.READ_WRITE)
     float32_image_R = attribute_wrapper(comms_annotation="numpy.float32 type read image (dims = 8x2)", datatype=numpy.float32, dims=(8, 2))
-    uint8_image_RW = attribute_wrapper(comms_annotation="numpy.uint8 type read/write image (dims = 2x8)", datatype=numpy.uint8, dims=(2, 8), access=AttrWriteType.READ_WRITE)
+    uint8_image_RW = attribute_wrapper(comms_annotation="numpy.uint8 type read/write image (dims = 2x8)", datatype=numpy.uint8, dims=(2, 8),
+                                       access=AttrWriteType.READ_WRITE)
 
     # --------
     # overloaded functions
@@ -68,16 +70,15 @@ class test_device(hardware_device):
 
         self.set_state(DevState.INIT)
 
-
-        #set up the OPC ua client
-        self.example_client = example_client(self.Standby, self.Fault, self)
+        # set up the test client
+        self.test_client = test_client(self.Fault, self)
 
         # map an access helper class
         for i in self.attr_list():
-            i.set_comm_client(self.example_client)
+            i.set_comm_client(self.test_client)
 
+        self.test_client.start()
 
-        self.example_client.start()
 
 # ----------
 # Run server
diff --git a/docker-compose/jupyter/Dockerfile b/docker-compose/jupyter/Dockerfile
index b9812188296627e9a5e2662007efa71ff78da90e..97ef7ca63daa60331ae0e8dee8f5d70fa143be44 100644
--- a/docker-compose/jupyter/Dockerfile
+++ b/docker-compose/jupyter/Dockerfile
@@ -25,3 +25,7 @@ ENV TINI_VERSION v0.6.0
 ENV JUPYTER_RUNTIME_DIR=/tmp
 ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini
 RUN sudo chmod +x /usr/bin/tini
+
+# Make sure Jupyter can write to the home directory
+ENV HOME=/home/tango
+RUN chmod a+rwx /home/tango