diff --git a/FofbTool/Configuration.py b/FofbTool/Configuration.py
new file mode 100644
index 0000000000000000000000000000000000000000..4ee6d4e4759afe1600d53b0dd1e3e16f37e7eea5
--- /dev/null
+++ b/FofbTool/Configuration.py
@@ -0,0 +1,384 @@
+###################################################################################################
+#    CONFIGURATION FUNCTIONS
+###################################################################################################
+#
+# Contains functions to configure blocks of the FOFB application.
+#
+###################################################################################################
+
+import tango
+import logging
+import configparser
+import numpy as np
+import FofbTool.Utils
+import os
+
+
+# Get the module logger
+logger = logging.getLogger("FofbTool")
+
+###################################################################################################
+#    CONFIGURATION PARSER
+###################################################################################################
+
+# Configuration
+config = configparser.ConfigParser()
+
+# Try to load default
+defconfpath = os.path.abspath(os.path.join(os.path.dirname(__spec__.origin), "default.cfg"))
+if len(config.read(defconfpath)) < 1:
+    logger.warning("Default configuration file not found: {}".format(defconfpath))
+
+def logdebugconf():
+    logger.debug("Display config:")
+    for s in config.sections():
+        for o in config.options(s):
+            logger.debug("{:20} {:10} {}".format(s,o, config[s][o]))
+
+def loadconfig(filepath):
+    """
+    Load a configuration file for the module.
+    It initialize the internal configuration.
+
+    PARAMETERS
+    ----------
+    filepath: str
+        Path to the config file
+    """
+    global config
+    logger.info("Load configuration from file {}".format(filepath))
+    if len(config.read(filepath)) < 1:
+        logger.warning("Configuration file not found: {}".format(filepath))
+
+
+def getconf(option, section, nodename="", t="", l=False):
+    """
+    Helper method to retrieve an option in the loaded configuration.
+    It tries to find a section named section.nodename and fall back to section only if fails.
+
+    PARAMETERS
+    ----------
+    option: str
+        Name of the option to retrieve
+    section: str
+        Section in wich to search
+    nodename: str
+        Specify a particular nodename. If not found in config, fall back to section default.
+    t: str ('i', 'f', 'b', '')
+        Give the type to cast, integer, float, boolean or string.
+    l: boolean
+        Cast from a list
+
+    RETURNS
+    -------
+    roption: str, int, float or boolean
+        Read option
+    """
+    if l:
+        if t=='b':
+            raise NotImplemented("List of boolean not implemented")
+        try:
+            val= np.asarray(config.get("{}.{}".format(section,nodename), option).split(), dtype=t)
+        except (configparser.NoSectionError, configparser.NoOptionError):
+            val= np.asarray(config.get(section, option).split(), dtype=t)
+
+    else:
+        try:
+            func = getattr(config, {'i':"getint", 'f':"getfloat", 'b':"getboolean", '':"get"}[t])
+        except KeyError as e:
+            log.debug(str(e))
+            raise ValueError("Allowed t argument are 'i', 'f', 'b' or ''")
+
+        try:
+            val= func("{}.{}".format(section,nodename), option)
+        except (configparser.NoSectionError, configparser.NoOptionError):
+            val= func(section, option)
+
+    return val
+
+
+
+###################################################################################################
+#    CONFIGURE COM BPM
+###################################################################################################
+
+# Filter list depends on the cellnode. These are the default values.
+bpmfilterlist = {
+        "cellnode-c01":np.array(list(range(1,23))+list(range(115,123))),
+        "cellnode-c06":np.array(range(23,53)),
+        "cellnode-c09":np.array(range(53,83)),
+        "cellnode-c14":np.array(range(83,115)),
+        }
+
+def cellnode_configure_combpm(cellnodename, bpmallowed=None):
+    """
+    Configure the combpm block of a CellNode.
+    The BPM data allowed through are either taken in the argument, or default to configuration.
+
+
+    PARAMETERS
+    ----------
+    cellnodename: str
+        The target cellnode, ie 'cellnode-c09'
+    bpmallowed: [int], None
+        List of BPMID allowed through the block. If None, default to nominal values.
+
+    RETURN
+    ------
+    success: boolean
+        True if configuration is a success
+    """
+
+    # Get device proxy
+    try:
+        p = FofbTool.Utils.tangopath_cellnodes[cellnodename.lower()]
+    except KeyError:
+        logger.error("Wrong cellnodename. Possibilities are {}".format(FofbTool.Utils.tangopath_cellnodes.keys()))
+        return False
+
+    try:
+        prx= tango.DeviceProxy(p)
+    except tango.DevFailed as e:
+        logger.error("Failed to get the Device proxy to '{}'".format(p))
+        logger.debug(str(e))
+        return False
+
+    # Select BPM id allowed to pass through
+    if bpmallowed is None:
+        logger.debug("Default to nominal values for BPM filter")
+        bpmallowed = getconf("bpmfilter", "combpm", cellnodename.lower(), 'i', True)
+
+
+    logger.debug("Activate BPMs {} in the filter for {}".format(bpmallowed, p))
+    f = prx["combpm_filter_table"].value
+    f[:] = 0
+    f[np.array(bpmallowed)] = 0x80
+    prx["combpm_filter_table"] = f
+
+    logger.info("Configuration of ComBpm done on {}.".format(p))
+    return True
+
+
+###################################################################################################
+#    CONFIGURE CCN: COM CELLNODES
+###################################################################################################
+
+
+def cellnode_configure_ccn(cellnodename, nbpm=None, npsc=None):
+    """
+    Configure the ComCellNode block on a cellnode.
+    Automatically set the number of bpm/psc packets and MAC length.
+
+    PARAMETERS
+    ----------
+    cellnodename: str
+        The target cellnode, ie 'cellnode-c09'
+    nbpm:
+        Number of BPM allowed by the filter, hence the number of expected BPM packets.
+        If None, auto detect the number from the combpm_filter_table attribute.
+    npsc:
+        Number of total PSC, hence the number of expected PSC packets.
+        Default to 100.
+
+    RETURN
+    ------
+    success: boolean
+        True if configuration is a success
+    """
+
+    # Get device proxy
+    try:
+        p = FofbTool.Utils.tangopath_cellnodes[cellnodename.lower()]
+    except KeyError:
+        logger.error("Wrong cellnodename. Possibilities are {}".format(FofbTool.Utils.tangopath_cellnodes.keys()))
+        return False
+
+    try:
+        prx= tango.DeviceProxy(p)
+    except tango.DevFailed as e:
+        logger.error("Failed to get the Device proxy to '{}'".format(p))
+        logger.debug(str(e))
+        return False
+
+    if nbpm is None:
+        logger.debug("Default to nominal values for number of BPM packets")
+        # Get number of BPM activated in the filter
+        nbpm = getconf("nbpm", "ccn", cellnodename, 'i')
+    logger.debug("{} bpm allowed in the ethernet frame on {}".format(nbpm, p))
+
+    if npsc is None:
+        logger.debug("Default to nominal values for number of PSC packets")
+        npsc = getconf("npsc", "ccn", cellnodename, 'i')
+    logger.debug("{} psc expected in the ethernet frame on {}".format(nbpm, p))
+
+    maclen = 10*nbpm+10
+    logger.debug("Configure packeter framesize (mac length) to {}".format(maclen))
+    prx["ccnpack0_framesize"] = maclen
+
+    logger.debug("Configure packeter npackets to {}".format(nbpm-1))
+    prx["ccnpack0_npackets"] = nbpm-1
+
+    maclen = npsc*6+10
+    logger.debug("Configure unpacketer framesize (mac length) to {}".format(maclen))
+    prx["ccnunpack0_framesize"] = maclen
+
+    logger.info("Configuration of CCN done on {}.".format(p))
+    return True
+
+def centralnode_configure_ccn(nbpm=None, npsc=None):
+    """
+    Configure the ComCellNode block on the centralnode.
+    Automatically set the number of bpm/psc packets and MAC length.
+
+    PARAMETERS
+    ----------
+    nbpm: list(int)
+        Number of BPM packet received on each interface.
+    npsc:
+        Number of total PSC, hence the number of expected PSC packets.
+
+    RETURN
+    ------
+    success: boolean
+        True if configuration is a success
+    """
+    p = FofbTool.Utils.tangopath_nodes["centralnode"]
+    try:
+        prx= tango.DeviceProxy(p)
+    except tango.DevFailed as e:
+        logger.error("Failed to get the Device proxy to '{}'".format(p))
+        logger.debug(str(e))
+        return False
+
+    if nbpm is None:
+        logger.debug("Use default value for nbpm")
+        nbpm = [getconf("nbpm", "ccn", n, 'i') for n in FofbTool.Utils.tangopath_cellnodes.keys()]
+    logger.debug("{} bpm expected in the ethernet frame on {}".format(nbpm, p))
+
+    if npsc is None:
+        logger.debug("Default to nominal values for number of BPM packets")
+        # Get number of BPM activated in the filter
+        npsc = getconf("npsc", "ccn", 'centralnode', 'i')
+    logger.debug("{} psc allowed in the ethernet frame on {}".format(nbpm, p))
+
+    for n in range(4):
+        maclen = npsc*6+10
+        logger.debug("Configure packeter {} framesize (mac length) to {}".format(n, maclen))
+        prx["ccnpack{}_framesize".format(n)] = maclen
+
+        logger.debug("Configure packeter {}  npackets to {}".format(n, npsc-1))
+        prx["ccnpack{}_npackets".format(n)] = npsc-1
+
+        maclen = 10*nbpm[n]+10
+        logger.debug("Configure unpacketer {} framesize (mac length) to {}".format(n, maclen))
+        prx["ccnunpack{}_framesize".format(n)] = maclen
+
+    logger.info("Configuration of CCN done on {}.".format(p))
+    return True
+
+###################################################################################################
+#    CONFIGURE COM CORR
+###################################################################################################
+
+def cellnode_configure_comcorr(cellnodename, pscid=None, enable=True):
+    """
+    Configure the comcorr block of a CellNode.
+    The PSC ID either taken in the argument, or default to nominal values.
+
+
+    PARAMETERS
+    ----------
+    cellnodename: str
+        The target cellnode, ie 'cellnode-c09'
+    pscid: [int], None
+        List of 32 PSCID to program on the output board. If None, default to nominal values.
+
+    enable: boolean
+        Enable or not the hardware outputs.
+
+    RETURN
+    ------
+    success: boolean
+        True if configuration is a success
+    """
+
+    # Get device proxy
+    try:
+        p = FofbTool.Utils.tangopath_cellnodes[cellnodename.lower()]
+    except KeyError:
+        logger.error("Wrong cellnodename. Possibilities are {}".format(FofbTool.Utils.tangopath_cellnodes.keys()))
+        return False
+
+    try:
+        prx= tango.DeviceProxy(p)
+    except tango.DevFailed as e:
+        logger.error("Failed to get the Device proxy to '{}'".format(p))
+        logger.debug(str(e))
+        return False
+
+    if pscid is None:
+        logger.debug("Default to nominal values for PSCID")
+        pscid = getconf("pscid", "comcorr", cellnodename, 'i', True)
+    else:
+        if len(pscid) != 32:
+            logger.error("pscid shall have length 32")
+            return False
+
+
+    logger.debug("Set PSCIDs {} in the filter for {}".format(pscid, p))
+    f= prx["comcorr_line_id"].value
+    f[:] = pscid
+    f[:] = f+ 0x10000*bool(enable)
+    logger.debug("Set comcorr_line_id {} for {}".format(f, p))
+    prx["comcorr_line_id"] = f
+
+    logger.info("Configuration of ComCorr done on {}.".format(p))
+    return True
+
+###################################################################################################
+#    CONFIGURE MATRIX CORRECTOR
+###################################################################################################
+
+def centralnode_configure_corr():
+    """
+    Configure the correction algorithm on the centralnode.
+    """
+    p = FofbTool.Utils.tangopath_nodes["centralnode"]
+    try:
+        prx= tango.DeviceProxy(p)
+    except tango.DevFailed as e:
+        logger.error("Failed to get the Device proxy to '{}'. Configuration of corrector algorithm failed.".format(p))
+        logger.debug(str(e))
+        return False
+
+    # Legacy
+    prx["corr_k1a_x"]=getconf( "k1a_x", "corr", t='i')
+    prx["corr_k1b_x"]=getconf( "k1b_x", "corr", t='i')
+    prx["corr_k1ic_x"]=getconf( "k1ic_x", "corr", t='i')
+    prx["corr_k1d_x"]=getconf( "k1d_x", "corr", t='i')
+
+    prx["corr_k1a_y"]=getconf( "k1a_y", "corr", t='i')
+    prx["corr_k1b_y"]=getconf( "k1b_y", "corr", t='i')
+    prx["corr_k1ic_y"]=getconf( "k1ic_y", "corr", t='i')
+    prx["corr_k1d_y"]=getconf( "k1d_y", "corr", t='i')
+
+    # Unitary
+    prx["corr_k2a_x"]=getconf( "k2a_x", "corr", t='i')
+    prx["corr_k2b_x"]=getconf( "k2b_x", "corr", t='i')
+    prx["corr_k2ic_x"]=getconf( "k2ic_x", "corr", t='i')
+    prx["corr_k2d_x"]=getconf( "k2d_x", "corr", t='i')
+
+    prx["corr_k2a_y"]=getconf( "k2a_y", "corr", t='i')
+    prx["corr_k2b_y"]=getconf( "k2b_y", "corr", t='i')
+    prx["corr_k2ic_y"]=getconf( "k2ic_y", "corr", t='i')
+    prx["corr_k2d_y"]=getconf( "k2d_y", "corr", t='i')
+
+    f= prx["corr_pscid"].value
+    f[0:100] = getconf('pscid', 'corr', t='i', l=True)
+    prx["corr_pscid"] = f
+
+    prx["corr_num_bpm"] = getconf('numbpm', 'corr', t='i')
+
+    logger.info("Configuration of Corr done on {}.".format(p))
+    return True
+
diff --git a/FofbTool.py b/FofbTool/DeviceAttributeConfiguration.py
old mode 100755
new mode 100644
similarity index 50%
rename from FofbTool.py
rename to FofbTool/DeviceAttributeConfiguration.py
index d737c77ce1f4d2e415d824720e818dbbf171bd5a..3a02e1e982fc4a9da9399b329b309034a0aa723a
--- a/FofbTool.py
+++ b/FofbTool/DeviceAttributeConfiguration.py
@@ -1,48 +1,30 @@
-#!/usr/bin/env python
-
-import tango
-import logging
-import argparse
-import numpy as np
-import time
-
-
 ###################################################################################################
-#    TANGO DEVICE PROXIES
-###################################################################################################
-
-cellnode_subscribers = [ tango.DeviceProxy("ans/dg/fofb-cellnode-{}".format(cell)) for cell in ("C01", "C06", "C09", "C14") ]
-
-centralnode_subscriber = tango.DeviceProxy("ans/dg/fofb-centralnode")
-
-bpmmanager = tango.DeviceProxy("ans/dg/bpm-manager")
-
-fofbcommand = tango.DeviceProxy("ans/dg/fofb-command")
-
+#    TANGO DB ATTRIBUTE CONFIG
 ###################################################################################################
-#    LOGGER
+#
+# This file contains crude functions to configure the attributes of the OPCUAProxy tango devices.
+#
 ###################################################################################################
-# Install a BasicLogger
-logger = logging.getLogger("TestDev")
-sh=logging.StreamHandler()
-sh.setLevel(logging.DEBUG)
-sh.setFormatter(logging.Formatter("{levelname:8}: {message}", style='{'))
 
 
-###################################################################################################
-#    TANGO DB ATTRIBUTE CONFIG
-###################################################################################################
+import logging
 
-def set_attr_config(cnp):
-    if "central" in cnp.name().lower():
-        set_attr_config_centralnode(cnp)
-    else:
-        set_attr_config_cellnode(cnp)
 
+# Get the module logger
+logger = logging.getLogger("FofbTool")
 
 def set_attr_config_cellnode(cnp):
+    """
+    Apply CellNode attribute configuration on the Tango Device cnp.
+    This does not check the kind of proxy passed. Expect exception if misuse.
+
+    PARAMETERS
+    ----------
+    cnp: tango.DeviceProxy
+        Device Proxy to the target device
+    """
 
-    logger.info("Apply attribute configuration on device {}".format(cnp.name()))
+    logger.info("Apply CellNode attribute configuration on device {}".format(cnp.name()))
 
     c = cnp.get_attribute_config("app_version")
     c.description = "FPGA firmware version\n" +c.description
@@ -176,7 +158,7 @@ def set_attr_config_cellnode(cnp):
     c.description = "ComCellNode ethernet status. Expected value 0x00\n" +c.description
     c.format = '0x%X'
     c.unit = ' '
-    c.alarms.max_warning='1'
+    c.alarms.max_warning='4'
     cnp.set_attribute_config(c)
 
     c = cnp.get_attribute_config("combpm_sfp")
@@ -234,8 +216,18 @@ def set_attr_config_cellnode(cnp):
     cnp.set_attribute_config(c)
 
 def set_attr_config_centralnode(cnp):
+    """
+    Apply Centralnode attribute configuration on the Tango Device cnp.
+    This does not check the kind of proxy passed. Expect exception if misuse.
+
+    PARAMETERS
+    ----------
+    cnp: tango.DeviceProxy
+        Device Proxy to the target device
+    """
 
-    logger.info("Apply attribute configuration on device {}".format(cnp.name()))
+
+    logger.info("Apply CentralNode attribute configuration on device {}".format(cnp.name()))
 
     c = cnp.get_attribute_config("app_version")
     c.description = "FPGA firmware version\n" +c.description
@@ -366,6 +358,15 @@ def set_attr_config_centralnode(cnp):
         c.alarms.max_warning='331'
         cnp.set_attribute_config(c)
 
+
+    c = cnp.get_attribute_config("corr_num_bpm")
+    c.description = "Number of expected bpm packets for the matrix multiplication."+c.description
+    c.format = '%d'
+    c.unit = ' '
+    c.alarms.min_warning='121'
+    c.alarms.max_warning='123'
+    cnp.set_attribute_config(c)
+
     for p in "xy":
 
         c = cnp.get_attribute_config("corr_control_{}".format(p))
@@ -376,6 +377,24 @@ def set_attr_config_centralnode(cnp):
         c.max_value='2'
         cnp.set_attribute_config(c)
 
+        c = cnp.get_attribute_config("corr_command_{}".format(p))
+        c.description = "Correction command sent for {} plan, in Ampers. Mobile mean on 1 second.\n".format(p.upper()) +c.description
+        c.format = '%f'
+        c.unit = 'A'
+        cnp.set_attribute_config(c)
+
+        c = cnp.get_attribute_config("orbit_error_{}".format(p))
+        c.description = "Orbit error read from BPMs, in nm. Mobile mean on 1 second.\n".format(p.upper()) +c.description
+        c.format = '%d'
+        c.unit = 'nm'
+        cnp.set_attribute_config(c)
+
+        c = cnp.get_attribute_config("corr_ref_{}".format(p))
+        c.description = "Orbit reference, in nm.\n".format(p.upper()) +c.description
+        c.format = '%d'
+        c.unit = 'nm'
+        cnp.set_attribute_config(c)
+
         for n in "12":
             for cf in ("a","b","ic","d"):
                 c = cnp.get_attribute_config("corr_k{}{}_{}".format(n,cf,p))
@@ -384,315 +403,3 @@ def set_attr_config_centralnode(cnp):
                 c.unit = ' '
                 cnp.set_attribute_config(c)
 
-
-
-###################################################################################################
-#    OPERATION FUNCTIONS
-###################################################################################################
-
-def cellnode_stop_ccn(cnp):
-    logger.info("Stopping CCN on {}".format(cnp.name()))
-    cnp["ccnpack0_control"] = False
-    cnp["ccnunpack0_control"] = False
-
-def cellnode_start_ccn(cnp):
-    logger.info("Starting CCN on {}".format(cnp.name()))
-    cnp["ccneth0_reset"] = 0x60000001 # impossible to write 0xE0000001
-    cnp["ccneth0_gt_reset"] = 1
-    cnp["ccneth0_gt_reset"] = 0
-    cnp["ccneth0_reset"] = 0
-    cnp["ccnpack0_control"] = True
-    cnp["ccnunpack0_control"] = True
-
-def cellnode_ack_ccn(snp):
-    logger.info("Ack CCN error on {}".format(cnp.name()))
-    cnp["ccnpack0_reset_error"] = True
-    cnp["ccnunpack0_reset_error"] = True
-    cnp["ccnpack0_reset_error"] = False
-    cnp["ccnunpack0_reset_error"] = False
-
-def centralnode_stop_ccn(cnp):
-    logger.info("Stopping CCN on {}".format(cnp.name()))
-    for n in range(4):
-        cnp["ccnpack{}_control".format(n)] = False
-        cnp["ccnunpack{}_control".format(n)] = False
-
-def centralnode_start_ccn(cnp):
-    logger.info("Starting CCN on {}".format(cnp.name()))
-    for n in range(4):
-        cnp["ccneth{}_reset".format(n)] = 0x60000001 # impossible to write 0xE0000001
-        cnp["ccneth{}_gt_reset".format(n)] = 1
-        cnp["ccneth{}_gt_reset".format(n)] = 0
-        cnp["ccneth{}_reset".format(n)] = 0
-        cnp["ccnpack{}_control".format(n)] = True
-        cnp["ccnunpack{}_control".format(n)] = True
-        cnp["ccnpack{}_reset_error".format(n)] = True
-        cnp["ccnunpack{}_reset_error".format(n)] = True
-        cnp["ccnpack{}_reset_error".format(n)] = False
-        cnp["ccnunpack{}_reset_error".format(n)] = False
-
-def cellnode_configure_ccn(cnp, nbpm=None, npsc=100):
-    """
-    Configure the ComCellNode block.
-
-    PARAMETERS
-    ----------
-    nbpm:
-        Number of BPM allowed by the filter, hence the number of expected BPM packets.
-        If None, auto detect the number from the combpm_filter_table attribute.
-    npsc:
-        Number of total PSC, hence the number of expected PSC packets.
-
-    """
-
-    if nbpm is None:
-        # Get number of BPM activated in the filter
-        nbpm = int((cnp["combpm_filter_table"].value == 0x80).sum())
-        logger.debug("{} bpm allowed in the filter on {}".format(nbpm, cnp.name()))
-
-    maclen = 10*nbpm+10
-    logger.debug("Configure packeter framesize (mac length) to {}".format(maclen))
-    cnp["ccnpack0_framesize"] = maclen
-
-    logger.debug("Configure packeter npackets to {}".format(nbpm-1))
-    cnp["ccnpack0_npackets"] = nbpm-1
-
-    maclen = npsc*6+10
-    logger.debug("Configure unpacketer framesize (mac length) to {}".format(maclen))
-    cnp["ccnunpack0_framesize"] = maclen
-
-    logger.info("Configuration of CCN done on {}.".format(cnp.name()))
-
-def centralnode_configure_ccn(cnp, nbpm=[30,30,30,32], npsc=100):
-    """
-    Configure the ComCellNode block.
-
-    PARAMETERS
-    ----------
-    nbpm: list(int)
-        Number of BPM packet received on each interface.
-    npsc:
-        Number of total PSC, hence the number of expected PSC packets.
-
-    """
-
-    for n in range(4):
-
-        maclen = npsc*6+10
-        logger.debug("Configure packeter {} framesize (mac length) to {}".format(n, maclen))
-        cnp["ccnpack{}_framesize".format(n)] = maclen
-
-        logger.debug("Configure packeter {}  npackets to {}".format(n, npsc-1))
-        cnp["ccnpack{}_npackets".format(n)] = npsc-1
-
-        maclen = 10*nbpm[n]+10
-        logger.debug("Configure unpacketer {} framesize (mac length) to {}".format(n, maclen))
-        cnp["ccnunpack{}_framesize".format(n)] = maclen
-
-    logger.info("Configuration of CCN done on {}.".format(cnp.name()))
-
-
-def cellnode_stop_combpm(cnp):
-    logger.info("Stopping ComBpm on {}".format(cnp.name()))
-    cnp["combpm_reset"] = 1
-    cnp["combpm_gt_control"] = 0x5
-
-def cellnode_start_combpm(cnp):
-    logger.info("Starting ComBpm on {}".format(cnp.name()))
-    cnp["combpm_reset"] = 0
-    cnp["combpm_gt_control"] = 0x1
-    cnp["combpm_reset_error"] = True
-    cnp["combpm_reset_error"] = False
-
-def cellnode_configure_combpm(cnp):
-
-    # Filter list depends on the cellnode
-    filterlist = {
-            "C01":np.array(list(range(1,23))+list(range(115,123))),
-            "C06":np.array(range(23,53)),
-            "C09":np.array(range(53,83)),
-            "C14":np.array(range(83,115)),
-            }
-
-    for k in filterlist.keys():
-        if k in cnp.name():
-            logger.debug("Activate BPMs {} in the filter for {}".format(filterlist[k], cnp.name()))
-            f = cnp["combpm_filter_table"].value
-            f[:] = 0
-            f[np.array(filterlist[k])] = 0x80
-            cnp["combpm_filter_table"] = f
-
-    logger.info("Configuration of ComBpm done on {}.".format(cnp.name()))
-
-def cellnode_configure_comcorr(cnp, enable=True):
-
-    pscidlist = {
-        'C01':[50,100,49,99,255,255,255,255,2,52,1,51,255,255,255,255,56,6,3,53,4,54,5,55,10,60,7,57,8,58,9,59],
-        'C06':[12,62,11,61,255,255,255,255,14,64,13,63,255,255,255,255,68,18,15,65,16,66,17,67,22,72,19,69,20,70,21,71],
-        'C09':[24,74,23,73,255,255,255,255,26,76,25,75,255,255,255,255,80,30,27,77,28,78,29,79,34,84,31,81,32,82,33,83],
-        'C14':[36,86,35,85,37,87,38,88,40,90,39,89,255,255,255,255,94,44,41,91,42,92,43,93,48,98,45,95,46,96,47,97]
-        }
-
-    for k in pscidlist.keys():
-        if k in cnp.name():
-            logger.debug("Set PSCIDs {} in the filter for {}".format(pscidlist[k], cnp.name()))
-            f= cnp["comcorr_line_id"].value
-            f[:] = pscidlist[k]
-            f[:] = f+ 0x10000*bool(enable)
-            logger.debug("Set comcorr_line_id {} for {}".format(f, cnp.name()))
-            cnp["comcorr_line_id"] = f
-
-    logger.info("Configuration of ComCorr done on {}.".format(cnp.name()))
-
-def centralnode_configure_corr(cnp):
-
-    # Legacy
-    cnp["corr_k1a_x"]=256
-    cnp["corr_k1b_x"]=0
-    cnp["corr_k1ic_x"]=64
-    cnp["corr_k1d_x"]=16300
-
-    cnp["corr_k1a_y"]=256
-    cnp["corr_k1b_y"]=0
-    cnp["corr_k1ic_y"]=64
-    cnp["corr_k1d_y"]=16300
-
-    # Unitary
-    cnp["corr_k2a_x"]=128
-    cnp["corr_k2b_x"]=0
-    cnp["corr_k2ic_x"]=8192
-    cnp["corr_k2d_x"]=0
-
-    cnp["corr_k2a_y"]=128
-    cnp["corr_k2b_y"]=0
-    cnp["corr_k2ic_y"]=8192
-    cnp["corr_k2d_y"]=0
-
-    f= cnp["corr_pscid"].value
-    f[0:100] = range(1,101)
-    cnp["corr_pscid"] = f
-
-    logger.info("Configuration of Corr done on {}.".format(cnp.name()))
-
-def fofbcommand_writeref(what='current'):
-    if what=="current":
-        fofbcommand["x_ref_orbit"] = (bpmmanager.xmeanorbit*1e6).astype(int)
-        fofbcommand["y_ref_orbit"] = (bpmmanager.zmeanorbit*1e6).astype(int)
-
-    if what=="oldref":
-        fofbcommand["x_ref_orbit"] = (bpmmanager.xreforbit*1e6).astype(int)
-        fofbcommand["y_ref_orbit"] = (bpmmanager.zreforbit*1e6).astype(int)
-
-    if what=="zeros":
-        fofbcommand["x_ref_orbit"] = np.zeros(122, dtype=int)
-        fofbcommand["y_ref_orbit"] = np.zeros(122, dtype=int)
-
-def fofbcommand_writemat(filename='respmat.npy'):
-
-    fofbcommand["x_inv_resp_mat"] = np.hstack([np.zeros((50, 1)), np.load("respmat.npy")[50:], np.zeros((50,5))]).astype(int)
-    fofbcommand["y_inv_resp_mat"] = np.hstack([np.zeros((50, 1)), np.load("respmat.npy")[:50], np.zeros((50,5))]).astype(int)
-
-###################################################################################################
-#
-###################################################################################################
-
-if __name__ == '__main__':
-
-    parser = argparse.ArgumentParser("FofbTool")
-    parser.add_argument("--log", default="info",
-            help="Log level (error, warning, info, debug)")
-    parser.add_argument("--init", action="store_true",
-            help="Run init devices.")
-    parser.add_argument("--confDS", action="store_true",
-            help="Configure the Device servers, by applying attribute configuration.")
-    parser.add_argument("--stop", action="store_true",
-            )
-    parser.add_argument("--conf", action="store_true",
-            )
-    parser.add_argument("--start", action="store_true",
-            )
-    parser.add_argument("--disable-corr", action="store_true",
-            help="Disable cellnode corrector output frames."
-            )
-    parser.add_argument("--respmat", default=None,
-            help="Configure response matrix from file"
-            )
-    parser.add_argument("node", default="allnodes", nargs='*',
-            choices=('c01', 'c06', 'c09', 'c14', 'central', 'allcells', 'allnodes'),
-            help="Node to apply the selected actions. Default is all.")
-
-    args = parser.parse_args()
-
-    #### Install logger
-    # Remove handler previously attached
-    for hdlr in logger.handlers:
-        logger.removeHandler(hdlr)
-    # Attach this handler
-    logger.addHandler(sh)
-    # Set log level from args
-    logger.setLevel(getattr(logging, args.log.upper()))
-    ####
-
-    logger.debug(args)
-
-
-    #### Node selection
-    nodes = []
-    if 'allnodes' in args.node:
-        nodes = cellnode_subscribers + [centralnode_subscriber,]
-    if 'allcells' in args.node:
-        nodes = nodes +  cellnode_subscribers
-    if 'central' in args.node:
-        nodes.append(centralnode_subscriber)
-    if 'c01' in args.node:
-        nodes.append(cellnode_subscribers[0])
-    if 'c06' in args.node:
-        nodes.append(cellnode_subscribers[1])
-    if 'c09' in args.node:
-        nodes.append(cellnode_subscribers[2])
-    if 'c14' in args.node:
-        nodes.append(cellnode_subscribers[3])
-
-    nodes = list(set(nodes))
-    logger.debug("Perform actions on {}".format(nodes))
-    ####
-
-    if args.init:
-        for cnp in nodes:
-            logger.info("Apply command init on {}".format(cnp.name()))
-            cnp.init()
-
-    if args.confDS:
-        for cnp in nodes:
-            set_attr_config(cnp)
-
-    if args.stop:
-        for cnp in cellnode_subscribers:
-            cellnode_stop_ccn(cnp)
-            cellnode_stop_combpm(cnp)
-        centralnode_stop_ccn(centralnode_subscriber)
-
-    if not args.respmat is None:
-        centralnode_configure_respmat(centralnode_subscriber, args.respmat)
-
-
-
-    if args.conf:
-        for cnp in cellnode_subscribers:
-            cellnode_configure_combpm(cnp)
-            cellnode_configure_comcorr(cnp, not args.disable_corr)
-            time.sleep(2)
-            cellnode_configure_ccn(cnp)
-        centralnode_configure_ccn(centralnode_subscriber)
-        centralnode_configure_corr(centralnode_subscriber)
-    elif args.disable_corr:
-        for cnp in cellnode_subscribers:
-            cellnode_configure_comcorr(cnp, not args.disable_corr)
-
-    if args.start:
-        for cnp in cellnode_subscribers:
-            cellnode_start_ccn(cnp)
-            cellnode_start_combpm(cnp)
-            cellnode_ack_ccn(cnp)
-        centralnode_start_ccn(centralnode_subscriber)
-
diff --git a/FofbTool/Operation.py b/FofbTool/Operation.py
new file mode 100644
index 0000000000000000000000000000000000000000..a4ce10cf2122e29f287ea509fe01402ebcb5922c
--- /dev/null
+++ b/FofbTool/Operation.py
@@ -0,0 +1,196 @@
+###################################################################################################
+#    OPERATION FUNCTIONS
+###################################################################################################
+#
+# Contains functions to operate the FOFB application.
+#
+###################################################################################################
+
+import tango
+import logging
+import numpy as np
+import time
+import FofbTool.Utils
+
+
+# Get the module logger
+logger = logging.getLogger("FofbTool")
+
+
+###################################################################################################
+#    OPERATIONS ON CCN
+###################################################################################################
+
+def stop_ccn(nodename):
+    """
+    Stop the communication with cellnode on the specified fofbnode.
+
+    PARAMETERS
+    ----------
+    nodename: str
+        The target fofbnode, ie 'cellnode-c09' or 'centralnode'
+
+    """
+    prx=FofbTool.Utils.get_prx_from_nodename(nodename)
+    if prx is None:
+        logger.error("Failed to stop CCN on {}".format(nodename))
+        return
+
+
+    logger.info("Stopping CCN on {}".format(nodename))
+    nint=1
+    if 'central' in nodename:
+        nint = 4
+    for n in range(nint):
+        prx["ccnpack{}_control".format(n)] = False
+        prx["ccnunpack{}_control".format(n)] = False
+
+def reset_ccn(nodename):
+    """
+    Reset the communication with cellnode on the specified fofbnode.
+
+    PARAMETERS
+    ----------
+    nodename: str
+        The target fofbnode, ie 'cellnode-c09' or 'centralnode'
+
+    """
+    prx=FofbTool.Utils.get_prx_from_nodename(nodename)
+    if prx is None:
+        logger.error("Failed to reset CCN on {}".format(nodename))
+        return
+
+    logger.info("Reset CCN on {}".format(nodename))
+    nint=1
+    if 'central' in nodename:
+        nint = 4
+    for n in range(nint):
+        prx["ccneth{}_reset".format(n)] = 0x60000001 # impossible to write 0xE0000001
+        prx["ccneth{}_gt_reset".format(n)] = 1
+
+    time.sleep(2)
+
+    for n in range(nint):
+        prx["ccneth{}_gt_reset".format(n)] = 0
+        prx["ccneth{}_reset".format(n)] = 0
+
+    ack_ccn(nodename)
+
+def start_ccn(nodename):
+    """
+    Start the communication with cellnode on the specified fofbnode.
+
+    PARAMETERS
+    ----------
+    nodename: str
+        The target fofbnode, ie 'cellnode-c09' or 'centralnode'
+
+    """
+    prx=FofbTool.Utils.get_prx_from_nodename(nodename)
+    if prx is None:
+        logger.error("Failed to start CCN on {}".format(nodename))
+        return
+
+    logger.info("Starting CCN on {}".format(nodename))
+    nint=1
+    if 'central' in nodename:
+        nint = 4
+    for n in range(nint):
+        prx["ccnpack{}_control".format(n)] = True
+        prx["ccnunpack{}_control".format(n)] = True
+
+def ack_ccn(nodename):
+    """
+    Start the communication with cellnode on the specified fofbnode.
+
+    PARAMETERS
+    ----------
+    nodename: str
+        The target fofbnode, ie 'cellnode-c09' or 'centralnode'
+
+    """
+    prx=FofbTool.Utils.get_prx_from_nodename(nodename)
+    if prx is None:
+        logger.error("Failed to ack CCN on {}".format(nodename))
+        return
+
+
+    logger.info("Ack CCN error on {}".format(nodename))
+    nint=1
+    if 'central' in nodename:
+        nint = 4
+    for n in range(nint):
+        prx["ccnpack{}_reset_error".format(n)] = True
+        prx["ccnunpack{}_reset_error".format(n)] = True
+
+    time.sleep(1)
+
+    for n in range(nint):
+        prx["ccnpack{}_reset_error".format(n)] = False
+        prx["ccnunpack{}_reset_error".format(n)] = False
+
+###################################################################################################
+#    OPERATIONS ON CCN
+###################################################################################################
+
+def stop_combpm(cellnodename):
+    """
+    Stop the communication with bpm on the specified cellnode.
+
+    PARAMETERS
+    ----------
+    cellnodename: str
+        The target fofbnode, ie 'cellnode-c09'
+
+    """
+    prx=FofbTool.Utils.get_prx_from_cellnodename(cellnodename)
+    if prx is None:
+        logger.error("Failed to stop ComBPM on {}".format(p))
+        return
+
+    logger.info("Stopping ComBPM on {}".format(cellnodename))
+    prx["combpm_reset"] = 1
+    prx["combpm_gt_control"] = 0x5
+
+def start_combpm(cellnodename):
+    """
+    Start the communication with bpm on the specified cellnode.
+
+    PARAMETERS
+    ----------
+    cellnodename: str
+        The target fofbnode, ie 'cellnode-c09'
+
+    """
+    prx=FofbTool.Utils.get_prx_from_cellnodename(cellnodename)
+    if prx is None:
+        logger.error("Failed to start ComBPM on {}".format(cellnodename))
+        return
+
+    logger.info("Starting ComBpm on {}".format(cellnodename))
+    prx["combpm_reset"] = 0
+    prx["combpm_gt_control"] = 0x1
+    time.sleep(1)
+    ack_combpm(cellnodename)
+
+
+def ack_combpm(cellnodename):
+    """
+    Ack errors on the communication with bpm on the specified cellnode.
+
+    PARAMETERS
+    ----------
+    cellnodename: str
+        The target fofbnode, ie 'cellnode-c09'
+
+    """
+    prx=FofbTool.Utils.get_prx_from_cellnodename(cellnodename)
+    if prx is None:
+        logger.error("Failed to start ComBPM on {}".format(cellnodename))
+        return
+
+    logger.info("Ack ComBpm on {}".format(cellnodename))
+    prx["combpm_reset_error"] = True
+    time.sleep(1)
+    prx["combpm_reset_error"] = False
+
diff --git a/FofbTool/Utils.py b/FofbTool/Utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..1e40081c6e7f46464032d25e8ae7834f6d34d221
--- /dev/null
+++ b/FofbTool/Utils.py
@@ -0,0 +1,297 @@
+###################################################################################################
+#    Utilities
+###################################################################################################
+#
+# This file contains usefull function and attributes to simplify life
+#
+###################################################################################################
+
+import tango
+import logging
+import FofbTool.DeviceAttributeConfiguration
+import FofbTool.Configuration
+import FofbTool.Operation
+
+# Get the module logger
+logger = logging.getLogger("FofbTool")
+
+tangopath_cellnodes = {
+        "cellnode-c01":"ans/dg/fofb-cellnode-c01",
+        "cellnode-c06":"ans/dg/fofb-cellnode-c06",
+        "cellnode-c09":"ans/dg/fofb-cellnode-c09",
+        "cellnode-c14":"ans/dg/fofb-cellnode-c14",
+        }
+
+tangopath_nodes = {
+        "centralnode":"ans/dg/fofb-centralnode",
+        }
+tangopath_nodes.update(tangopath_cellnodes)
+
+tangopath = {
+        "fofb-watcher":"ans/dg/fofb-watcher",
+        "fofb-command":"ans/dg/fofb-command",
+        "fofb-manager":"ans/dg/fofb-manager",
+        "bpm-manager":"ans/dg/bpm-manager",
+        }
+tangopath.update(tangopath_nodes)
+
+def init_opcua():
+    """
+    Run init on all OPCUA devices. Catch DevFailed and inform via log.
+    """
+
+    for i,(n,p) in enumerate(tangopath_nodes.items()):
+        logger.info("Perform init() on {} '{}'".format(n,p))
+        try:
+            tango.DeviceProxy(p).init()
+        except tango.DevFailed as e:
+            logger.error("Could not perform init() '{}', got DevFailed.".format(p))
+            logger.debug(str(e))
+
+def init_watcher():
+    """
+    Run init on Fofb-Watcher, waiting for its completion then init on the FofbCommand.
+    """
+
+    wprx=tango.DeviceProxy(tangopath["fofb-watcher"])
+    wprx.set_timeout_millis(30000)
+
+    logger.info("Perform init() on Fofb-Watcher. This takes nearly a minute.")
+    try:
+        wprx.init()
+    except tango.DevFailed as e:
+        logger.error("Could not perform init() on Fofb-Watcher.")
+
+    logger.info("Perform init() on Fofb-Command.")
+    tango.DeviceProxy(tangopath["fofb-command"]).init()
+
+def confds_opcua():
+    """
+    Apply attribute configuration on all OPCUA devices. Catch DevFailed and inform via log.
+    """
+    for i,(n,p) in enumerate(tangopath_nodes.items()):
+        try:
+            prx = tango.DeviceProxy(p)
+        except tango.DevFailed as e:
+            logger.error("Could not get proxy '{}', got DevFailed.".format(p))
+            logger.debug(str(e))
+            break
+
+        try:
+            if 'central' in n:
+                FofbTool.DeviceAttributeConfiguration.set_attr_config_centralnode(prx)
+            else:
+                FofbTool.DeviceAttributeConfiguration.set_attr_config_cellnode(prx)
+        except tango.DevFailed as e:
+            logger.error("Could not set attribute configuration for '{}', got DevFailed.".format(p))
+            logger.debug(str(e))
+
+
+def check_fofbnotrunning():
+    """
+    Check if the FOFB is not running.
+    If it fails to know, return False and log error.
+
+    RETURN
+    ------
+    running: Boolean
+        True if FOFB is not running
+    """
+
+    try:
+        prx = tango.DeviceProxy(tangopath["fofb-watcher"])
+    except tango.DevFailed as e:
+        logger.error("Failed to get the Device proxy to '{}'".format(tangopath["fofb-watcher"]))
+        logger.debug(str(e))
+        return False
+
+    try:
+        return not (prx.fofbrunning_x or prx.fofbrunning_y)
+    except tango.DevFailed as e:
+        logger.error("Failed to read the FOFB status on device Fofb-Watcher")
+        logger.debug(str(e))
+        return False
+
+def conf_all_combpm():
+    """
+    Run default configuration of all combpm blocks.
+    Check beforehand that the FOFB is not running.
+
+    RETURN
+    ------
+    running: Boolean
+        True if FOFB is not running
+    """
+
+    if not check_fofbnotrunning():
+        logger.warning("Not running configuration of combpm because FOFB seems to be running.")
+        return False
+
+    success=True
+    for i,(n,p) in enumerate(tangopath_cellnodes.items()):
+        s=FofbTool.Configuration.cellnode_configure_combpm(n)
+        success = success and s
+
+    return success
+
+
+def conf_all_comcorr():
+    """
+    Run default configuration of all comcorr blocks.
+    Check beforehand that the FOFB is not running.
+
+    RETURN
+    ------
+    running: Boolean
+        True if FOFB is not running
+    """
+
+    if not check_fofbnotrunning():
+        logger.warning("Not running configuration of comcorr because FOFB seems to be running.")
+        return False
+
+    success=True
+    for i,(n,p) in enumerate(tangopath_cellnodes.items()):
+        s=FofbTool.Configuration.cellnode_configure_comcorr(n)
+        success = success and s
+
+    return success
+
+
+def conf_all_ccn():
+    """
+    Run default configuration of all comcellnode blocks.
+    Check beforehand that the FOFB is not running.
+
+    RETURN
+    ------
+    running: Boolean
+        True if FOFB is not running
+    """
+
+    if not check_fofbnotrunning():
+        logger.warning("Not running configuration of comcorr because FOFB seems to be running.")
+        return False
+
+    success=True
+    for i,(n,p) in enumerate(tangopath_cellnodes.items()):
+        s=FofbTool.Configuration.cellnode_configure_ccn(n)
+        success = success and s
+
+    s=FofbTool.Configuration.centralnode_configure_ccn()
+    success = success and s
+
+    return success
+
+def get_prx_from_nodename(nodename):
+    """
+    Return a tango.DeviceProxy from a node name.
+    On failure, log error and return None.
+
+    PARAMETERS
+    ----------
+    nodename: str
+        The target fofbnode, ie 'cellnode-c09' or 'centralnode'
+
+    RETURN
+    ------
+    prx: tango.DeviceProxy or None
+    """
+    # Get device proxy
+    try:
+        p = FofbTool.Utils.tangopath_nodes[nodename.lower()]
+    except KeyError:
+        logger.error("Wrong nodename. Possibilities are {}".format(FofbTool.Utils.tangopath_nodes.keys()))
+        return None
+
+    try:
+        prx= tango.DeviceProxy(p)
+    except tango.DevFailed as e:
+        logger.error("Failed to get the Device proxy to '{}'".format(p))
+        logger.debug(str(e))
+        return None
+
+    return prx
+
+
+
+def get_prx_from_cellnodename(cellnodename):
+    """
+    Return a tango.DeviceProxy from a cellnode name.
+    On failure, log error and return None.
+
+    PARAMETERS
+    ----------
+    nodename: str
+        The target fofbnode, ie 'cellnode-c09'
+
+    RETURN
+    ------
+    prx: tango.DeviceProxy or None
+    """
+    # Get device proxy
+    try:
+        p = FofbTool.Utils.tangopath_cellnodes[cellnodename.lower()]
+    except KeyError:
+        logger.error("Wrong nodename. Possibilities are {}".format(FofbTool.Utils.tangopath_cellnodes.keys()))
+        return None
+
+    try:
+        prx= tango.DeviceProxy(p)
+    except tango.DevFailed as e:
+        logger.error("Failed to get the Device proxy to '{}'".format(p))
+        logger.debug(str(e))
+        return None
+
+    return prx
+
+def stop_all_combpm():
+    """
+    Apply stop command on all Cellnodes.
+    Check beforehand that the FOFB is not running. Display warning if.
+
+    """
+
+    if not check_fofbnotrunning():
+        logger.warning("Not running stop combpm because FOFB seems to be running.")
+
+    for i,(n,p) in enumerate(tangopath_cellnodes.items()):
+        FofbTool.Operation.stop_combpm(n)
+
+
+def stop_all_ccn():
+    """
+    Apply stop and reset commands on all fofbnodes.
+    Check beforehand that the FOFB is not running. Display warning if.
+
+    """
+
+    if not check_fofbnotrunning():
+        logger.warning("Not running stop combpm because FOFB seems to be running.")
+
+    for i,(n,p) in enumerate(tangopath_nodes.items()):
+        FofbTool.Operation.stop_ccn(n)
+        FofbTool.Operation.reset_ccn(n)
+
+
+
+def start_all_combpm():
+    """
+    Apply start command on all Cellnodes.
+
+    """
+
+    for i,(n,p) in enumerate(tangopath_cellnodes.items()):
+        FofbTool.Operation.start_combpm(n)
+
+
+def start_all_ccn():
+    """
+    Apply stop and reset commands on all fofbnodes.
+
+    """
+
+    for i,(n,p) in enumerate(tangopath_nodes.items()):
+        FofbTool.Operation.start_ccn(n)
+
+
diff --git a/FofbTool/__init__.py b/FofbTool/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/FofbTool/__init__.py
@@ -0,0 +1 @@
+
diff --git a/FofbTool/default.cfg b/FofbTool/default.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..1841bf1ffbb817a7c0e0c79b67c613252fbd3be0
--- /dev/null
+++ b/FofbTool/default.cfg
@@ -0,0 +1,92 @@
+[combpm]
+
+[combpm.cellnode-c01]
+bpmfilter = 1 2 3 4 5 6 7 8 9 10
+            11 12 13 14 15 16 17 18 19 20
+            21 22 115 116 117 118 119 120 121 122
+
+[combpm.cellnode-c06]
+bpmfilter = 23 24 25 26 27 28 29 30 31 32
+            33 34 35 36 37 38 39 40 41 42
+            43 44 45 46 47 48 49 50 51 52
+
+[combpm.cellnode-c09]
+bpmfilter = 53 54 55 56 57 58 59 60 61 62
+            63 64 65 66 67 68 69 70 71 72
+            73 74 75 76 77 78 79 80 81 82
+
+[combpm.cellnode-c14]
+bpmfilter = 83 84 85 86 87 88 89 90 91 92
+            93 94 95 96 97 98 99 100 101 102
+            103 104 105 106 107 108 109 110 111 112 113 114
+
+[ccn]
+npsc = 100
+nbpm = 30
+
+[ccn.cellnode-c14]
+nbpm = 32
+
+
+[comcorr.cellnode-c01]
+pscid = 50 100 49 99
+        255 255 255 255
+        2 52 1 51
+        255 255 255 255
+        56 6 3 53
+        4 54 5 55
+        10 60 7 57
+        8 58 9 59
+
+[comcorr.cellnode-c06]
+pscid = 12 62 11 61
+        255 255 255 255
+        14 64 13 63
+        255 255 255 255
+        68 18 15 65
+        16 66 17 67
+        22 72 19 69
+        20 70 21 71
+
+[comcorr.cellnode-c09]
+pscid = 24 74 23 73
+        255 255 255 255
+        26 76 25 75
+        255 255 255 255
+        80 30 27 77
+        28 78 29 79
+        34 84 31 81
+        32 82 33 83
+
+[comcorr.cellnode-c14]
+pscid = 36 86 35 85
+        37 87 38 88
+        40 90 39 89
+        255 255 255 255
+        94 44 41 91
+        42 92 43 93
+        48 98 45 95
+        46 96 47 97
+
+[corr]
+k1a_x = 256
+k1b_x = 0
+k1ic_x = 64
+k1d_x = 16300
+k1a_y = 256
+k1b_y = 0
+k1ic_y = 64
+k1d_y = 16300
+k2a_x = 128
+k2b_x = 0
+k2ic_x = 8192
+k2d_x = 0
+k2a_y = 128
+k2b_y = 0
+k2ic_y = 8192
+k2d_y = 0
+pscid = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
+        26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
+        51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
+        76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
+numbpm = 122
diff --git a/README.adoc b/README.adoc
index 347e2e1c1dbf1a941196ef90e8e1a41b0cfba32e..74193f2e6305f0874cdd790d525fb2e1090aa67c 100644
--- a/README.adoc
+++ b/README.adoc
@@ -1,9 +1,21 @@
-= FofbTool script
+= FofbTool Module
 
-This expert script helps to configure, stop and start the communication between the FofbNodes.
+This python module provides functions to deal with the Fofb MTCA platform.
+It connects to the OPCUAProxy tango devices.
 
+The module is packaged in sections
+* FofbTool.Configuration: For application configuration
+* FofbTool.Operation: Function to perform operation (start, stop...)
+* FofbTool.DeviceAttributeConfiguration: contains functions to apply tango attribute configuration
 
-= Basic usages
+There is also a Command Line Interface bin/FofbTool written in python.
+
+To deploy on the RCM, move into dir and execute install_rcm.sh.
+
+
+
+
+= Command Line Interface: Basic usages
 
 == Classic: stop, config, start
 
@@ -15,79 +27,62 @@ Then apply the default configuration on FPGA blocks:
 * ComCellNode (Expected packets, correct MAC size...)
 * ComBPM (BPM ID filter Filter for each CellNode...)
 * ComCorr (Default PSCID on output...)
-* Corr (Default correction filter...)
 
 IMPORTANT: This do not configure Orbit Reference or Inv Response Matrix.
 This do not start the FOFB, only the communication.
 
-WARNING: PSC will receive frames upon start. Better stop the FOFB beforehand.
-
+WARNING: PSC will receive frames upon start. Better stop the FOFB beforehand. The CLI won't let you do that.
 
-[%collapsible%open]
-====
 [source,console]
 ----
-$ FofbTool --stop --config --start
-INFO    : Stopping CCN on ANS/DG/FOFB-CELLNODE-C01
-INFO    : Stopping ComBpm on ANS/DG/FOFB-CELLNODE-C01
-INFO    : Stopping CCN on ANS/DG/FOFB-CELLNODE-C06
-INFO    : Stopping ComBpm on ANS/DG/FOFB-CELLNODE-C06
-INFO    : Stopping CCN on ANS/DG/FOFB-CELLNODE-C09
-INFO    : Stopping ComBpm on ANS/DG/FOFB-CELLNODE-C09
-INFO    : Stopping CCN on ANS/DG/FOFB-CELLNODE-C14
-INFO    : Stopping ComBpm on ANS/DG/FOFB-CELLNODE-C14
-INFO    : Stopping CCN on ANS/DG/FOFB-CENTRALNODE
-INFO    : Configuration of ComBpm done on ANS/DG/FOFB-CELLNODE-C01.
-INFO    : Configuration of ComCorr done on ANS/DG/FOFB-CELLNODE-C01.
-INFO    : Configuration of CCN done on ANS/DG/FOFB-CELLNODE-C01.
-INFO    : Configuration of ComBpm done on ANS/DG/FOFB-CELLNODE-C06.
-INFO    : Configuration of ComCorr done on ANS/DG/FOFB-CELLNODE-C06.
-INFO    : Configuration of CCN done on ANS/DG/FOFB-CELLNODE-C06.
-INFO    : Configuration of ComBpm done on ANS/DG/FOFB-CELLNODE-C09.
-INFO    : Configuration of ComCorr done on ANS/DG/FOFB-CELLNODE-C09.
-INFO    : Configuration of CCN done on ANS/DG/FOFB-CELLNODE-C09.
-INFO    : Configuration of ComBpm done on ANS/DG/FOFB-CELLNODE-C14.
-INFO    : Configuration of ComCorr done on ANS/DG/FOFB-CELLNODE-C14.
-INFO    : Configuration of CCN done on ANS/DG/FOFB-CELLNODE-C14.
-INFO    : Configuration of CCN done on ANS/DG/FOFB-CENTRALNODE.
-INFO    : Configuration of Corr done on ANS/DG/FOFB-CENTRALNODE.
-INFO    : Starting CCN on ANS/DG/FOFB-CELLNODE-C01
-INFO    : Starting ComBpm on ANS/DG/FOFB-CELLNODE-C01
-INFO    : Ack CCN error on ANS/DG/FOFB-CELLNODE-C01
-INFO    : Starting CCN on ANS/DG/FOFB-CELLNODE-C06
-INFO    : Starting ComBpm on ANS/DG/FOFB-CELLNODE-C06
-INFO    : Ack CCN error on ANS/DG/FOFB-CELLNODE-C06
-INFO    : Starting CCN on ANS/DG/FOFB-CELLNODE-C09
-INFO    : Starting ComBpm on ANS/DG/FOFB-CELLNODE-C09
-INFO    : Ack CCN error on ANS/DG/FOFB-CELLNODE-C09
-INFO    : Starting CCN on ANS/DG/FOFB-CELLNODE-C14
-INFO    : Starting ComBpm on ANS/DG/FOFB-CELLNODE-C14
-INFO    : Ack CCN error on ANS/DG/FOFB-CELLNODE-C14
-INFO    : Starting CCN on ANS/DG/FOFB-CENTRALNODE
+$ FofbTool --stop --config every --start
 ----
 
-====
 
 == Configure OPCUAProxy DS
 
 
 Upon restart of OPCUAProxy Tango DS, to reapply attribute configuration (units, alarm level, descriptions...)
 
-[%collapsible%open]
-====
 [source,console]
 ----
-$ FofbTool --confDS
-INFO    : Apply attribute configuration on device ANS/DG/FOFB-CELLNODE-C09
-INFO    : Apply attribute configuration on device ANS/DG/FOFB-CELLNODE-C14
-INFO    : Apply attribute configuration on device ANS/DG/FOFB-CENTRALNODE
-INFO    : Apply attribute configuration on device ANS/DG/FOFB-CELLNODE-C06
-INFO    : Apply attribute configuration on device ANS/DG/FOFB-CELLNODE-C01
-----
+$ FofbTool --DS-conf
 
-====
+----
 
+== More help
 
+[source,console]
+----
+usage: FofbTool [-h] [--log LOG] [--conf-file CONF_FILE] [--DS-init-opcua]
+                [--DS-init-watcher] [--DS-conf] [--stop] [--stop-combpm]
+                [--stop-ccn]
+                [--configure {combpm,ccn,comcorr,corr,all,every} [{combpm,ccn,comcorr,corr,all,every} ...]]
+                [--start] [--start-combpm] [--start-ccn]
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --log LOG             Log level (error, warning, info, debug)
+  --conf-file CONF_FILE
+                        Path to config file to apply.
+  --DS-init-opcua       Run init on opcua devices.
+  --DS-init-watcher     Run init on the Fofb-Watcher device, and then the
+                        Fofb-Command.
+  --DS-conf             Applying attribute configuration on Tango Device
+                        Servers. This is required after restart of devices.
+  --stop                Stop command for the CCN and ComBPM applications
+                        blocks.
+  --stop-combpm         Stop command for the ComBPM applications blocks.
+  --stop-ccn            Stop command for the ComBPM applications blocks.
+  --configure {combpm,ccn,comcorr,corr,all,every} [{combpm,ccn,comcorr,corr,all,every} ...]
+                        Configuration commands for the Fofb applications
+                        blocs. 'all' is for all com, not configuring
+                        corrector. 'every' configure everything.
+  --start               Start command for the CCN and ComBPM applications
+                        blocks.
+  --start-combpm        Start command for the ComBPM applications blocks.
+  --start-ccn           Start command for the ComBPM applications blocks.
+----
 
 
 
diff --git a/bin/FofbTool b/bin/FofbTool
new file mode 100755
index 0000000000000000000000000000000000000000..b50d5859c99c9f0f38321e3306ad91b6ea8fac3e
--- /dev/null
+++ b/bin/FofbTool
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+###################################################################################################
+#   COMMAND LINE INTERFACE
+###################################################################################################
+#
+# This file gives the Command Line Interface for FofbTool
+#
+###################################################################################################
+
+if __name__ == '__main__':
+    import logging
+    import argparse
+    import os
+    import sys
+
+    # Safer import: add the parent dir in path
+    sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+    import FofbTool.Utils
+    import FofbTool.Configuration # Still needs for one function, TODO
+
+
+    # Get the module logger
+    logger = logging.getLogger("FofbTool")
+
+    # Add a Stream handler to the module logger
+    sh=logging.StreamHandler()
+    sh.setLevel(logging.DEBUG)
+    sh.setFormatter(logging.Formatter("{levelname:8}: {message}", style='{'))
+    logger.addHandler(sh)
+
+    parser = argparse.ArgumentParser("FofbTool")
+    parser.add_argument("--log", default="info",
+            help="Log level (error, warning, info, debug)")
+
+    parser.add_argument("--conf-file", type=str,
+            help="Path to config file to apply.")
+
+    parser.add_argument("--DS-init-opcua", action="store_true",
+            help="Run init on opcua devices.")
+    parser.add_argument("--DS-init-watcher", action="store_true",
+            help="Run init on the Fofb-Watcher device, and then the Fofb-Command.")
+    parser.add_argument("--DS-conf", action="store_true",
+            help="Applying attribute configuration on Tango Device Servers. This is required after restart of devices.")
+
+    parser.add_argument("--stop", action="store_true",
+            help="Stop command for the CCN and ComBPM applications blocks.")
+    parser.add_argument("--stop-combpm", action="store_true",
+            help="Stop command for the ComBPM applications blocks.")
+    parser.add_argument("--stop-ccn", action="store_true",
+            help="Stop command for the ComBPM applications blocks.")
+
+    parser.add_argument("--configure", choices=["combpm", "ccn", "comcorr", "corr", "all", "every"], nargs="+",
+            help="Configuration commands for the Fofb applications blocs."+
+            " 'all' is for all com, not configuring corrector. 'every' configure everything.")
+
+    parser.add_argument("--start", action="store_true",
+            help="Start command for the CCN and ComBPM applications blocks.")
+    parser.add_argument("--start-combpm", action="store_true",
+            help="Start command for the ComBPM applications blocks.")
+    parser.add_argument("--start-ccn", action="store_true",
+            help="Start command for the ComBPM applications blocks.")
+
+
+    args = parser.parse_args()
+
+    # Set log level from args
+    logger.setLevel(getattr(logging, args.log.upper()))
+
+    logger.debug(args)
+
+    if "conf_file" in args:
+        FofbTool.Configuration.loadconfig(args.conf_file)
+    FofbTool.Configuration.logdebugconf()
+
+    ## Device Server related commands
+    if args.DS_init_opcua:
+        FofbTool.Utils.init_opcua()
+
+    if args.DS_init_watcher:
+        FofbTool.Utils.init_watcher()
+
+    if args.DS_conf:
+        FofbTool.Utils.confds_opcua()
+
+    if args.stop or args.stop_combpm:
+        FofbTool.Utils.stop_all_combpm()
+    if args.stop or args.stop_ccn:
+        FofbTool.Utils.stop_all_ccn()
+
+    if not args.configure is None:
+        for conf in args.configure:
+            if conf in ("combpm", "all", "every"):
+                FofbTool.Utils.conf_all_combpm()
+            if conf in ("comcorr", "all", "every"):
+                FofbTool.Utils.conf_all_comcorr()
+            if conf in ("ccn", "all", "every"):
+                FofbTool.Utils.conf_all_ccn()
+            if conf in ("corr", "every"):
+                FofbTool.Configuration.centralnode_configure_corr()
+
+    if args.start or args.start_combpm:
+        FofbTool.Utils.start_all_combpm()
+    if args.start or args.start_ccn:
+        FofbTool.Utils.start_all_ccn()
+
diff --git a/install_rcm.sh b/install_rcm.sh
new file mode 100755
index 0000000000000000000000000000000000000000..76018c9fe1d57f9c5707501b4d7a9d4ffc44e764
--- /dev/null
+++ b/install_rcm.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+echo "Deploy module and CLI in RCM environment"
+
+# Copy Python module
+ipath=/home/operateur/.local/lib/python3.6/site-packages/FofbTool
+mkdir -p $ipath
+cp FofbTool/*.py $ipath/ -rvf
+
+# Copy bin CLI
+cp bin/FofbTool ~/bin/ -vf
+
+# Put exec rights
+chmod +x ~/bin/FofbTool