From 0f4785319905b0e0ede0ac0bc96a933030040c3b Mon Sep 17 00:00:00 2001
From: "m.marn" <m.marn@gsi.de>
Date: Mon, 18 Sep 2023 08:42:59 +0000
Subject: [PATCH] Refactor: Integration of PLC Actions into Block model

Closes #22

See merge request silecs/opensilecs!53
---
 .../interface/core/PLCRecvAction.cpp          | 403 ----------
 .../interface/core/PLCRecvAction.h            |  74 --
 .../interface/core/PLCSendAction.cpp          | 382 ----------
 .../interface/core/PLCSendAction.h            |  74 --
 .../interface/equipment/PLCBlock.cpp          | 719 +++++++++++++++++-
 .../interface/equipment/PLCBlock.h            |  45 +-
 .../interface/equipment/SilecsBlock.cpp       |  11 -
 .../interface/equipment/SilecsBlock.h         |  32 +-
 .../interface/equipment/SilecsCluster.cpp     |   4 +-
 .../interface/equipment/SilecsCluster.h       |   4 +-
 .../interface/equipment/SilecsDevice.cpp      |   8 +-
 .../interface/equipment/SilecsDevice.h        |   7 +-
 .../interface/equipment/SilecsPLC.cpp         |  54 +-
 .../interface/equipment/SilecsPLC.h           |   4 +-
 14 files changed, 744 insertions(+), 1077 deletions(-)
 delete mode 100644 silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.cpp
 delete mode 100644 silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.h
 delete mode 100644 silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.cpp
 delete mode 100644 silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.h

diff --git a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.cpp b/silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.cpp
deleted file mode 100644
index 85a4c0f..0000000
--- a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.cpp
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
-Copyright (c) 2017 European Organization for Nuclear Research (CERN).
-All rights reserved. This program and the accompanying materials
-are made available under the terms of the GNU Public License v3.0
-which accompanies this distribution, and is available at
-http://www.gnu.org/licenses/gpl.html
-
-Contributors:
-    .European Organization for Nuclear Research    (CERN) - initial API and implementation
-    .GSI Helmholtzzentrum für Schwerionenforschung (GSI)  - features and bugfixes
-*/
-
-
-#include <silecs-communication/interface/core/SilecsService.h>
-#include <silecs-communication/interface/core/PLCAction.h>
-#include <silecs-communication/interface/core/PLCRecvAction.h>
-#include <silecs-communication/interface/communication/SilecsConnection.h>
-#include <silecs-communication/interface/equipment/SilecsPLC.h>
-#include <silecs-communication/interface/equipment/SilecsDevice.h>
-#include <silecs-communication/interface/equipment/SilecsBlock.h>
-#include <silecs-communication/interface/equipment/PLCBlock.h>
-#include <silecs-communication/interface/core/Context.h>
-#include <silecs-communication/interface/utility/SilecsLog.h>
-
-namespace Silecs
-{
-
-PLCRecvBlockMode::PLCRecvBlockMode(Block* block) :
-                PLCAction(block)
-{
-    if (DEBUG & Log::topics_)
-        LOG(ALLOC) << "Action PLCRecvBlockMode (create): " << block->getName();
-}
-
-PLCRecvBlockMode::~PLCRecvBlockMode()
-{
-    if (DEBUG & Log::topics_)
-        LOG(ALLOC) << "Action PLCRecvBlockMode (delete): " << theBlock_->getName();
-}
-
-PLCRecvDeviceMode::PLCRecvDeviceMode(Block* block) :
-                PLCAction(block)
-{
-    if (DEBUG & Log::topics_)
-        LOG(ALLOC) << "Action PLCRecvDeviceMode (create): " << theBlock_->getName();
-}
-
-PLCRecvDeviceMode::~PLCRecvDeviceMode()
-{
-    if (DEBUG & Log::topics_)
-        LOG(ALLOC) << "Action PLCRecvDeviceMode (delete): " << theBlock_->getName();
-}
-
-int PLCRecvBlockMode::execute(Context* pContext)
-{
-    timeval tod; //Time-of-day for register time-stamping
-    int errorCode = 0;
-
-    Connection* pConn = theBlock_->getPLC()->getConnection();
-    AccessArea area = theBlock_->getAccessArea();
-
-    try
-    {
-        //if transaction is for one device only, pContext contains its reference
-        if (pContext->isForOneDevice())
-        {
-            //Device context: get block of one device only ====================================
-            Device* pDev = pContext->getDevice();
-
-            //Base attributes of the device within the complete data block containing all devices.
-            unsigned long usedAddress = theBlock_->getAddress();
-            unsigned long usedSize = theBlock_->getMemSize();
-            unsigned long usedDeviceOffset = pDev->getInputAddress(area) * usedSize;
-            unsigned char* pBuffer = ((unsigned char*)theBlock_->getBuffer()) + usedDeviceOffset;
-
-            // Overwrite device-block address, offset & size in case user wants to resize the block dynamically
-            if (theBlock_->withCustomAttributes() == true)
-            {
-                usedAddress = theBlock_->getCustomAddress();
-                usedSize = theBlock_->getCustomSize();
-                usedDeviceOffset = theBlock_->getCustomOffset();
-            }
-
-            if (RECV & Log::topics_)
-            {
-                if (pConn->isEnabled())
-                {
-                    Log(RECV).getLog() << "RecvAction (execute/ BlockMode): " << ", device: " << pDev->getLabel() << ", block: " << theBlock_->getName();
-                }
-            }
-            // Try to open the connection, if it fails. Throw an exception. On success all the
-            // PLC blocks will be updated this function will be called recursively within doOpen.
-            // Once all blocks are updated continue from here.
-            if (!pConn->doOpen(theBlock_->getPLC()))
-            {
-                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
-            }
-
-            //begin critical section
-            std::lock_guard<std::mutex> lock(bufferMux_);
-            switch (theBlock_->getAccessArea())
-            {
-                case AccessArea::Digital:
-                    errorCode = pConn->readDIO(theBlock_->getPLC(), usedAddress, //Base address (or DBn) of the block
-                    usedDeviceOffset, //Device data address within the block
-                    usedSize, //Get one device-block only
-                    pBuffer //Buffer to store the data
-                    );
-                    break;
-                case AccessArea::Analog:
-                {
-                    errorCode = pConn->readAIO(theBlock_->getPLC(), usedAddress, //Base address (or DBn) of the block
-                    usedDeviceOffset, //Device data address within the block
-                    usedSize, //Get one device-block only
-                    pBuffer //Buffer to store the data
-                    );
-                    break;
-                }
-                case AccessArea::Memory:
-                default:
-                {
-                    errorCode = pConn->readMemory(theBlock_->getPLC(), usedAddress, //Base address (or DBn) of the block
-                    usedDeviceOffset, //Device data address within the block
-                    usedSize, //Get one device-block only
-                    pBuffer //Buffer to store the data
-                    );
-                    break;
-                }
-            }
-
-            if (pConn->isConnected() && (errorCode == 0))
-            { //Data have just been received: get time-of-day to time-stamp the registers
-                gettimeofday(&tod, 0);
-
-                //Device context: Extract all the device registers from that block
-                pDev->importRegisters(theBlock_, pBuffer, tod, pContext);
-
-                LOG_DELAY(RECV) << "done for block: " << theBlock_->getName();
-            }
-            //end critical section
-        }
-        else
-        {
-            //Dynamic resizing of the block is not possible in BLOCK_MODE
-            if (theBlock_->withCustomAttributes() == true)
-                throw SilecsException(__FILE__, __LINE__, COMM_BLOCK_RESIZING_NOT_ALLOWED);
-
-            deviceVectorType::iterator pDeviceIter;
-            deviceVectorType& deviceCol = theBlock_->getPLC()->getDeviceMap();
-
-            //Cluster/PLC context: get block of all devices in one go =========================
-            if (RECV & Log::topics_)
-            {
-                if (pConn->isEnabled())
-                {
-                    Log(RECV).getLog() << "RecvAction (execute/ BlockMode): " << theBlock_->getName() << ", " << deviceCol.size() << " device(s)";
-                }
-            }
-
-            // Try to open the connection, if it fails. Throw an exception. On success all the
-            // PLC blocks will be updated this function will be called recursively within doOpen.
-            // Once all blocks are updated continue from here.
-            if (!pConn->doOpen(theBlock_->getPLC()))
-            {
-                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
-            }
-
-            //begin critical section
-            std::lock_guard<std::mutex> lock(bufferMux_);
-            switch (area)
-            {
-                case AccessArea::Digital:
-                    errorCode = pConn->readDIO(theBlock_->getPLC(), theBlock_->getAddress(), //Base address (or DBn) of the block
-                    0, //Get all devices from the first one
-                    ((PLCBlock*)theBlock_)->getBufferSize(), //Get blocks of all devices (full buffer size)
-                    (unsigned char*)theBlock_->getBuffer() //Buffer to store the data (full buffer)
-                    );
-                    break;
-                case AccessArea::Analog:
-                {
-                    errorCode = pConn->readAIO(theBlock_->getPLC(), theBlock_->getAddress(), //Base address (or DBn) of the block
-                    0, //Get all devices from the first one
-                    ((PLCBlock*)theBlock_)->getBufferSize(), //Get blocks of all devices (full buffer size)
-                    (unsigned char*)theBlock_->getBuffer() //Buffer to store the data (full buffer)
-                    );
-                    break;
-                }
-                case AccessArea::Memory:
-                default:
-                {
-                    errorCode = pConn->readMemory(theBlock_->getPLC(), theBlock_->getAddress(), //Base address (or DBn) of the block
-                    0, //Get all devices from the first one
-                    ((PLCBlock*)theBlock_)->getBufferSize(), //Get blocks of all devices (full buffer size)
-                    (unsigned char*)theBlock_->getBuffer() //Buffer to store the data (full buffer)
-                    );
-                    break;
-                }
-            }
-
-            if (pConn->isConnected() && (errorCode == 0))
-            { //Data have just been received: get time-of-day to time-stamp the registers
-                gettimeofday(&tod, 0);
-
-                //PLC context: Extract all registers value of all devices of the PLC from that block
-                for (pDeviceIter = deviceCol.begin(); pDeviceIter != deviceCol.end(); ++pDeviceIter)
-                {
-                    Device* pDev = pDeviceIter->second;
-
-                    // Only extract if this device actually contains this block, otherwise skip it.
-                    if (!pDev->hasBlock(theBlock_->getName()))
-                    {
-                        continue;
-                    }
-                    unsigned long deviceOffset = pDev->getInputAddress(area) * theBlock_->getMemSize();
-                    unsigned char* pBuffer = ((unsigned char*)theBlock_->getBuffer()) + deviceOffset;
-                    pDev->importRegisters(theBlock_, pBuffer, tod, pContext);
-                }
-
-                LOG_DELAY(RECV) << "done for block: " << theBlock_->getName();
-            }
-        }
-    }
-    catch(const SilecsException& ex)
-    {
-        LOG(ERROR) << ex.what();
-        LOG(ERROR) << "RecvAction (execute/ BlockMode) for block " << theBlock_->getName() << " has failed.";
-        return ex.getCode();
-    }
-    return 0;
-}
-
-int PLCRecvDeviceMode::execute(Context* pContext)
-{
-    timeval tod; //Time-of-day for register time-stamping
-    int errorCode = 0;
-    Connection* pConn = theBlock_->getPLC()->getConnection();
-    AccessArea area = theBlock_->getAccessArea();
-
-    try
-    {
-        //if transaction is for one device only, pContext contains its reference
-        if (pContext->isForOneDevice())
-        {
-            //Device context: get block of one device only ====================================
-            Device* pDev = pContext->getDevice();
-
-            //Set base attributes of the device blocks
-            unsigned long usedDeviceAddress = pDev->getInputAddress(area);
-            unsigned long usedBlockAddress = theBlock_->getAddress();
-            unsigned long usedSize = theBlock_->getMemSize();
-
-            // Overwrite device-block address, offset & size in case user wants to resize the block dynamically
-            if (theBlock_->withCustomAttributes() == true)
-            {
-                usedDeviceAddress = theBlock_->getCustomAddress();
-                usedBlockAddress = theBlock_->getCustomOffset();
-                usedSize = theBlock_->getCustomSize();
-            }
-
-            if (RECV & Log::topics_)
-            {
-                if (pConn->isEnabled())
-                {
-                    Log(RECV).getLog() << "RecvAction (execute/ DeviceMode): device: " << pDev->getLabel() << ", block: " << theBlock_->getName();
-                }
-            }
-
-            // Try to open the connection, if it fails. Throw an exception. On success all the
-            // PLC blocks will be updated this function will be called recursively within doOpen.
-            // Once all blocks are updated continue from here.
-            if (!pConn->doOpen(theBlock_->getPLC()))
-            {
-                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
-            }
-
-            //begin critical section
-            std::lock_guard<std::mutex> lock(bufferMux_);
-            switch (theBlock_->getAccessArea())
-            {
-                case AccessArea::Digital:
-                    errorCode = pConn->readDIO(theBlock_->getPLC(), usedDeviceAddress, //Base address (or DBn) of the device
-                    usedBlockAddress, //Block data address within the device
-                    usedSize, //Get one device-block only
-                    (unsigned char*)theBlock_->getBuffer() //Buffer to store the data
-                    );
-                    break;
-                case AccessArea::Analog:
-                {
-                    errorCode = pConn->readAIO(theBlock_->getPLC(), usedDeviceAddress, //Base address (or DBn) of the device
-                    usedBlockAddress, //Block data address within the device
-                    usedSize, //Get one device-block only
-                    (unsigned char*)theBlock_->getBuffer() //Buffer to store the data
-                    );
-                    break;
-                }
-                case AccessArea::Memory:
-                default:
-                {
-                    errorCode = pConn->readMemory(theBlock_->getPLC(), usedDeviceAddress, //Base address (or DBn) of the device
-                    usedBlockAddress, //Block data address within the device
-                    usedSize, //Get one device-block only
-                    (unsigned char*)theBlock_->getBuffer() //Buffer to store the data
-                    );
-                    break;
-                }
-            }
-
-            if (pConn->isConnected() && (errorCode == 0))
-            { //Data have just been received: get time-of-day to time-stamp the registers
-                gettimeofday(&tod, 0);
-
-                //Device context: Extract all the device registers from that block
-                pDev->importRegisters(theBlock_, (unsigned char*)theBlock_->getBuffer(), tod, pContext);
-
-                LOG_DELAY(RECV) << "done for block: " << theBlock_->getName();
-            }
-        }
-        else
-        {
-            //Dynamic resizing of the block is not possible in multiple access
-            if (theBlock_->withCustomAttributes() == true)
-                throw SilecsException(__FILE__, __LINE__, COMM_BLOCK_RESIZING_NOT_ALLOWED);
-
-            //Cluster/PLC context: get block of all devices one by one =========================
-            deviceVectorType::iterator pDeviceIter;
-            deviceVectorType& deviceCol = theBlock_->getPLC()->getDeviceMap();
-            for (pDeviceIter = deviceCol.begin(); pDeviceIter != deviceCol.end(); ++pDeviceIter)
-            {
-                Device* pDev = pDeviceIter->second;
-                if (!pDev->hasBlock(theBlock_->getName()))
-                {
-                    continue;
-                }
-
-                if (RECV & Log::topics_)
-                {
-                    if (pConn->isEnabled())
-                    {
-                        Log(RECV).getLog() << "RecvAction (execute/ DeviceMode): device: " << pDev->getLabel() << ", block: " << theBlock_->getName();
-                    }
-                }
-
-                // Try to open the connection, if it fails. Throw an exception. On success all the
-                // PLC blocks will be updated this function will be called recursively within doOpen.
-                // Once all blocks are updated continue from here.
-                if (!pConn->doOpen(theBlock_->getPLC()))
-                {
-                    throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
-                }
-                //begin critical section
-                std::lock_guard<std::mutex> lock(bufferMux_);
-                switch (area)
-                {
-                    case AccessArea::Digital:
-                        errorCode = pConn->readDIO(theBlock_->getPLC(), pDev->getInputAddress(area), //Base address (or DBn) of the device
-                        theBlock_->getAddress(), //Block data address within the device
-                        theBlock_->getMemSize(), //Get one device-block only
-                        (unsigned char*)theBlock_->getBuffer() //Buffer to store the data
-                        );
-                        break;
-                    case AccessArea::Analog:
-                    {
-                        errorCode = pConn->readAIO(theBlock_->getPLC(), pDev->getInputAddress(area), //Base address (or DBn) of the device
-                        theBlock_->getAddress(), //Block data address within the device
-                        theBlock_->getMemSize(), //Get one device-block only
-                        (unsigned char*)theBlock_->getBuffer() //Buffer to store the data
-                        );
-                        break;
-                    }
-                    case AccessArea::Memory:
-                    default:
-                    {
-                        errorCode = pConn->readMemory(theBlock_->getPLC(), pDev->getInputAddress(area), //Base address (or DBn) of the device
-                        theBlock_->getAddress(), //Block data address within the device
-                        theBlock_->getMemSize(), //Get one device-block only
-                        (unsigned char*)theBlock_->getBuffer() //Buffer to store the data
-                        );
-                        break;
-                    }
-                }
-
-                if (pConn->isConnected() && (errorCode == 0))
-                { //Data have just been received: get time-of-day to time-stamp the registers
-                    gettimeofday(&tod, 0);
-
-                    //PLC context: Extract all registers value of the current device of the PLC from that block
-                    pDev->importRegisters(theBlock_, (unsigned char*)theBlock_->getBuffer(), tod, pContext);
-
-                    LOG_DELAY(RECV) << "done for block: " << theBlock_->getName();
-                }
-            }
-        }
-    }
-    catch(const SilecsException& ex)
-    {
-        LOG(ERROR) << ex.what();
-        LOG(ERROR) << "RecvAction (execute/ DeviceMode) failed.";
-        return ex.getCode();
-    }
-    return 0;
-}
-
-} // namespace
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.h b/silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.h
deleted file mode 100644
index 766619b..0000000
--- a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-Copyright (c) 2017 European Organization for Nuclear Research (CERN).
-All rights reserved. This program and the accompanying materials
-are made available under the terms of the GNU Public License v3.0
-which accompanies this distribution, and is available at
-http://www.gnu.org/licenses/gpl.html
-
-Contributors:
-    .European Organization for Nuclear Research    (CERN) - initial API and implementation
-    .GSI Helmholtzzentrum für Schwerionenforschung (GSI)  - features and bugfixes
-*/
-
-
-#ifndef _RECV_PLC_ACTION_H_
-#define _RECV_PLC_ACTION_H_
-
-#include <silecs-communication/interface/core/PLCAction.h>
-
-namespace Silecs
-{
-class Action;
-class Block;
-class Context;
-
-/*!
- * \class PLCRecvBlockMode
- * \brief Concrete action responsible to get a data block from the PLC (Block mode configuration)
- */
-class PLCRecvBlockMode : public PLCAction
-{
-
-public:
-    PLCRecvBlockMode(Block* block);
-    virtual ~PLCRecvBlockMode();
-
-    /*!
-     * \fn execute
-     * Connect the PLC if necessary and Get data-block from its memory.
-     * Overwrite the abstract method
-     * \return 0 if no error occurred. else return a error status from low level library
-     */
-    int execute(Context* pContext);
-
-private:
-
-};
-
-/*!
- * \class PLCRecvDeviceMode
- * \brief Concrete action responsible to get a data block from the PLC (Device mode configuration)
- */
-class PLCRecvDeviceMode : public PLCAction
-{
-
-public:
-    PLCRecvDeviceMode(Block* block);
-    virtual ~PLCRecvDeviceMode();
-
-    /*!
-     * \fn execute
-     * Connect the PLC if necessary and Get data-block from its memory.
-     * Overwrite the abstract method
-     * \return 0 if no error occurred. else return a error status from low level library
-     */
-    int execute(Context* pContext);
-
-private:
-
-};
-
-} // namespace
-
-#endif // _RECV_PLC_ACTION_H_
-
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.cpp b/silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.cpp
deleted file mode 100644
index 59dc5e3..0000000
--- a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.cpp
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
-Copyright (c) 2017 European Organization for Nuclear Research (CERN).
-All rights reserved. This program and the accompanying materials
-are made available under the terms of the GNU Public License v3.0
-which accompanies this distribution, and is available at
-http://www.gnu.org/licenses/gpl.html
-
-Contributors:
-    .European Organization for Nuclear Research    (CERN) - initial API and implementation
-    .GSI Helmholtzzentrum für Schwerionenforschung (GSI)  - features and bugfixes
-*/
-
-
-#include <silecs-communication/interface/core/SilecsService.h>
-#include <silecs-communication/interface/core/SilecsAction.h>
-#include <silecs-communication/interface/core/PLCAction.h>
-#include <silecs-communication/interface/core/PLCSendAction.h>
-#include <silecs-communication/interface/communication/SilecsConnection.h>
-#include <silecs-communication/interface/equipment/SilecsPLC.h>
-#include <silecs-communication/interface/equipment/SilecsDevice.h>
-#include <silecs-communication/interface/equipment/SilecsBlock.h>
-#include <silecs-communication/interface/equipment/PLCBlock.h>
-#include <silecs-communication/interface/core/Context.h>
-#include <silecs-communication/interface/utility/SilecsLog.h>
-
-namespace Silecs
-{
-
-PLCSendBlockMode::PLCSendBlockMode(Block* block) :
-                PLCAction(block)
-{
-    if (DEBUG & Log::topics_)
-        LOG(ALLOC) << "Action PLCSendBlockMode (constructor): " << block->getName();
-}
-
-PLCSendBlockMode::~PLCSendBlockMode()
-{
-    if (DEBUG & Log::topics_)
-        LOG(ALLOC) << "Action PLCSendBlockMode (destructor): " << theBlock_->getName();
-}
-
-PLCSendDeviceMode::PLCSendDeviceMode(Block* block) :
-                PLCAction(block)
-{
-    if (DEBUG & Log::topics_)
-        LOG(ALLOC) << "Action PLCSendDeviceMode (constructor): " << theBlock_->getName();
-}
-
-PLCSendDeviceMode::~PLCSendDeviceMode()
-{
-    if (DEBUG & Log::topics_)
-        LOG(ALLOC) << "Action PLCSendDeviceMode (destructor): " << theBlock_->getName();
-}
-
-int PLCSendBlockMode::execute(Context* pContext)
-{
-    int errorCode = 0;
-    Connection* pConn = theBlock_->getPLC()->getConnection();
-
-    try
-    {
-        //if transaction is for one device only, pContext contains its reference
-        if (pContext->isForOneDevice())
-        {
-            //Device context: send block of one device only ====================================
-            Device* pDev = pContext->getDevice();
-
-            //Base attributes of the device within the complete data block containing all devices.
-            unsigned long usedAddress = theBlock_->getAddress();
-            unsigned long usedSize = theBlock_->getMemSize();
-            AccessArea area = theBlock_->getAccessArea();
-            unsigned long usedDeviceOffset = pDev->getOutputAddress(area) * usedSize;
-            unsigned char* pBuffer = ((unsigned char*)theBlock_->getBuffer()) + usedDeviceOffset;
-
-            // Overwrite device-block address, offset & size in case user wants to resize the block dynamically
-            if (theBlock_->withCustomAttributes() == true)
-            {
-                usedAddress = theBlock_->getCustomAddress();
-                usedSize = theBlock_->getCustomSize();
-                usedDeviceOffset = theBlock_->getCustomOffset();
-            }
-
-            // Try to open the connection, if it fails. Throw an exception. On success all the
-            // PLC blocks will be updated this function will be called recursively within doOpen.
-            // Once all blocks are updated continue from here.
-            if (!pConn->doOpen(theBlock_->getPLC()))
-            {
-                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
-            }
-            { //critical section
-                std::lock_guard<std::mutex> lock(bufferMux_);
-                if (pConn->isEnabled())
-                {
-                    LOG(SEND) << "SendAction (execute/ BlockMode): device: " << pDev->getLabel() << ", block: " << theBlock_->getName();
-
-                    //Device context: Export all the device registers to that block
-                    pDev->exportRegisters(theBlock_, pBuffer, pContext);
-                }
-
-                switch (area)
-                {
-                    case AccessArea::Digital:
-                        errorCode = pConn->writeDIO(theBlock_->getPLC(), usedAddress, //Base address (or DBn) of the block
-                            usedDeviceOffset, //Device data address within the block
-                            usedSize, //Set one device-block only
-                            pBuffer //Buffer which contain the data
-                            );
-                        break;
-                    case AccessArea::Analog:
-                    {
-                        errorCode = pConn->writeAIO(theBlock_->getPLC(), usedAddress, //Base address (or DBn) of the block
-                            usedDeviceOffset, //Device data address within the block
-                            usedSize, //Set one device-block only
-                            pBuffer //Buffer which contain the data
-                            );
-                        break;
-                    }
-                    case AccessArea::Memory:
-                    default:
-                        errorCode = pConn->writeMemory(theBlock_->getPLC(), usedAddress, //Base address (or DBn) of the block
-                            usedDeviceOffset, //Device data address within the block
-                            usedSize, //Set one device-block only
-                            pBuffer //Buffer which contain the data
-                            );
-                        break;
-                }
-            }
-
-            if (SEND & Log::topics_)
-            {
-                if (pConn->isEnabled())
-                {
-                    Log(SEND).getLogDelay() << "done for block: " << theBlock_->getName();
-                }
-            }
-        }
-        else
-        {
-            //Dynamic resizing of the block is not possible in BLOCK_MODE
-            if (theBlock_->withCustomAttributes() == true)
-                throw SilecsException(__FILE__, __LINE__, COMM_BLOCK_RESIZING_NOT_ALLOWED);
-
-            // Try to open the connection, if it fails. Throw an exception. On success all the
-            // PLC blocks will be updated this function will be called recursively within doOpen.
-            // Once all blocks are updated continue from here.
-            if (!pConn->doOpen(theBlock_->getPLC()))
-            {
-                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
-            }
-
-            { //critical section
-                std::lock_guard<std::mutex> lock(bufferMux_);
-                if (pConn->isEnabled())
-                {
-                    deviceVectorType::iterator pDeviceIter;
-                    deviceVectorType& deviceCol = theBlock_->getPLC()->getDeviceMap();
-
-                    //Cluster/PLC context: set block of all devices in one go ===========================
-                    LOG(SEND) << "SendAction (execute/ BlockMode): block: " << theBlock_->getName() << ", " << deviceCol.size() << " device(s)";
-
-                    //PLC context: Export all registers value of all devices of the PLC to that block
-                    for (pDeviceIter = deviceCol.begin(); pDeviceIter != deviceCol.end(); ++pDeviceIter)
-                    {
-                        Device* pDev = pDeviceIter->second;
-                        AccessArea area = theBlock_->getAccessArea();
-                        unsigned long deviceOffset = pDev->getOutputAddress(area) * theBlock_->getMemSize();
-                        unsigned char* pBuffer = ((unsigned char*)theBlock_->getBuffer()) + deviceOffset;
-                        pDev->exportRegisters(theBlock_, pBuffer, pContext);
-                    }
-                }
-
-                switch (theBlock_->getAccessArea())
-                {
-                    case AccessArea::Digital:
-                        errorCode = pConn->writeDIO(theBlock_->getPLC(), theBlock_->getAddress(), //Base address (or DBn) of the block
-                            0, //Set all devices from the first one
-                            ((PLCBlock*)theBlock_)->getBufferSize(), //Set blocks of all devices (full buffer size)
-                            (unsigned char*)theBlock_->getBuffer() //Buffer which contain the data (full buffer)
-                            );
-                        break;
-                    case AccessArea::Analog:
-                    {
-                        errorCode = pConn->writeAIO(theBlock_->getPLC(), theBlock_->getAddress(), //Base address (or DBn) of the block
-                            0, //Set all devices from the first one
-                            ((PLCBlock*)theBlock_)->getBufferSize(), //Set blocks of all devices (full buffer size)
-                            (unsigned char*)theBlock_->getBuffer() //Buffer which contain the data (full buffer)
-                            );
-                        break;
-                    }
-                    case AccessArea::Memory:
-                    default:
-                        errorCode = pConn->writeMemory(theBlock_->getPLC(), theBlock_->getAddress(), //Base address (or DBn) of the block
-                            0, //Set all devices from the first one
-                            ((PLCBlock*)theBlock_)->getBufferSize(), //Set blocks of all devices (full buffer size)
-                            (unsigned char*)theBlock_->getBuffer() //Buffer which contain the data (full buffer)
-                            );
-                        break;
-                }
-            }
-
-            if (SEND & Log::topics_)
-            {
-                if (pConn->isEnabled())
-                {
-                    Log(SEND).getLogDelay() << "done for block: " << theBlock_->getName();
-                }
-            }
-        }
-    }
-    catch(const SilecsException& ex)
-    {
-        LOG(ERROR) << ex.what();
-        LOG(ERROR) << "SendAction (execute/ DeviceMode) on block: " << theBlock_->getName() << " has failed";
-        return ex.getCode();
-    }
-    return errorCode;
-}
-
-int PLCSendDeviceMode::execute(Context* pContext)
-{
-    Connection* pConn = theBlock_->getPLC()->getConnection();
-    int errorCode = 0;
-    try
-    {
-        //if transaction is for one device only, pContext contains its reference
-        if (pContext->isForOneDevice())
-        {
-            //Device context: set block of one device only ====================================
-            Device* pDev = pContext->getDevice();
-
-            //Set base attributes of the device blocks
-            AccessArea area = theBlock_->getAccessArea();
-            unsigned long usedDeviceAddress = pDev->getOutputAddress(area);
-            unsigned long usedBlockAddress = theBlock_->getAddress();
-            unsigned long usedSize = theBlock_->getMemSize();
-
-            // Overwrite device-block address, offset & size in case user wants to resize the block dynamically
-            if (theBlock_->withCustomAttributes() == true)
-            {
-                usedDeviceAddress = theBlock_->getCustomAddress();
-                usedBlockAddress = theBlock_->getCustomOffset();
-                usedSize = theBlock_->getCustomSize();
-            }
-
-            // Try to open the connection, if it fails. Throw an exception. On success all the
-            // PLC blocks will be updated this function will be called recursively within doOpen.
-            // Once all blocks are updated continue from here.
-            if (!pConn->doOpen(theBlock_->getPLC()))
-            {
-                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
-            }
-
-            { //critical section
-                std::lock_guard<std::mutex> lock(bufferMux_);
-                if (pConn->isEnabled())
-                {
-                    LOG(SEND) << "SendAction (execute/ DeviceMode): device: " << pDev->getLabel() << ", block: " << theBlock_->getName();
-
-                    //Device context: Export all the device registers to that block
-                    pDev->exportRegisters(theBlock_, (unsigned char*)theBlock_->getBuffer(), pContext);
-                }
-
-                switch (area)
-                {
-                    case AccessArea::Digital:
-                        errorCode = pConn->writeDIO(theBlock_->getPLC(), usedDeviceAddress, //Base address (or DBn) of the device
-                            usedBlockAddress, //Block data address within the device
-                            usedSize, //Set one device-block only
-                            (unsigned char*)theBlock_->getBuffer() //Buffer which contain the data
-                            );
-                        break;
-                    case AccessArea::Analog:
-                    {
-                        errorCode = pConn->writeAIO(theBlock_->getPLC(), usedDeviceAddress, //Base address (or DBn) of the device
-                            usedBlockAddress, //Block data address within the device
-                            usedSize, //Set one device-block only
-                            (unsigned char*)theBlock_->getBuffer() //Buffer which contain the data
-                            );
-                        break;
-                    }
-                    case AccessArea::Memory:
-                    default:
-                        errorCode = pConn->writeMemory(theBlock_->getPLC(), usedDeviceAddress, //Base address (or DBn) of the device
-                            usedBlockAddress, //Block data address within the device
-                            usedSize, //Set one device-block only
-                            (unsigned char*)theBlock_->getBuffer() //Buffer which contain the data
-                            );
-                        break;
-                }
-            }
-
-            if (SEND & Log::topics_)
-            {
-                if (pConn->isEnabled())
-                {
-                    Log(SEND).getLogDelay() << "done for block: " << theBlock_->getName();
-                }
-            }
-        }
-        else
-        {
-            //Dynamic resizing of the block is not possible in multiple access
-            if (theBlock_->withCustomAttributes() == true)
-                throw SilecsException(__FILE__, __LINE__, COMM_BLOCK_RESIZING_NOT_ALLOWED);
-
-            //Cluster/PLC context: set block of all devices one by one =========================
-            deviceVectorType::iterator pDeviceIter;
-            deviceVectorType& deviceCol = theBlock_->getPLC()->getDeviceMap();
-            for (pDeviceIter = deviceCol.begin(); pDeviceIter != deviceCol.end(); ++pDeviceIter)
-            {
-                Device* pDev = pDeviceIter->second;
-                if (!pDev->hasBlock(theBlock_->getName()))
-                {
-                    continue;
-                }
-                // Try to open the connection, if it fails. Throw an exception. On success all the
-                // PLC blocks will be updated this function will be called recursively within doOpen.
-                // Once all blocks are updated continue from here.
-                if (!pConn->doOpen(theBlock_->getPLC()))
-                {
-                    throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
-                }
-                { //critical section
-                    std::lock_guard<std::mutex> lock(bufferMux_);
-                    if (pConn->isEnabled())
-                    {
-                        LOG(SEND) << "SendAction (execute/ DeviceMode):  device: " << pDev->getLabel() << ", block: " << theBlock_->getName();
-
-                        //PLC context: Export all registers value of the current device of the PLC to that block
-                        pDev->exportRegisters(theBlock_, (unsigned char*)theBlock_->getBuffer(), pContext);
-                    }
-
-                    AccessArea area = theBlock_->getAccessArea();
-                    switch (area)
-                    {
-                        case AccessArea::Digital:
-                            errorCode = pConn->writeDIO(theBlock_->getPLC(), pDev->getOutputAddress(area), //Base address (or DBn) of the device
-                                theBlock_->getAddress(), //Block data address within the device
-                                theBlock_->getMemSize(), //Set one device-block only
-                                (unsigned char*)theBlock_->getBuffer() //Buffer which contain the data
-                                );
-                            break;
-                        case AccessArea::Analog:
-                        {
-                            errorCode = pConn->writeAIO(theBlock_->getPLC(), pDev->getOutputAddress(area), //Base address (or DBn) of the device
-                                theBlock_->getAddress(), //Block data address within the device
-                                theBlock_->getMemSize(), //Set one device-block only
-                                (unsigned char*)theBlock_->getBuffer() //Buffer which contain the data
-                                );
-                            break;
-                        }
-                        case AccessArea::Memory:
-                        default:
-                            errorCode = pConn->writeMemory(theBlock_->getPLC(), pDev->getOutputAddress(area), //Base address (or DBn) of the device
-                                theBlock_->getAddress(), //Block data address within the device
-                                theBlock_->getMemSize(), //Set one device-block only
-                                (unsigned char*)theBlock_->getBuffer() //Buffer which contain the data
-                                );
-                            break;
-                    }
-                }
-
-                if (SEND & Log::topics_)
-                {
-                    if (pConn->isEnabled())
-                    {
-                        Log(SEND).getLogDelay() << "done for block: " << theBlock_->getName();
-                    }
-                }
-            }
-        }
-    }
-    catch(const SilecsException& ex)
-    {
-        LOG(ERROR) << ex.what();
-        LOG(ERROR) << "SendAction (execute/ DeviceMode) failed";
-        return ex.getCode();
-    }
-    return errorCode;
-}
-
-} // namespace
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.h b/silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.h
deleted file mode 100644
index 43b2aff..0000000
--- a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-Copyright (c) 2017 European Organization for Nuclear Research (CERN).
-All rights reserved. This program and the accompanying materials
-are made available under the terms of the GNU Public License v3.0
-which accompanies this distribution, and is available at
-http://www.gnu.org/licenses/gpl.html
-
-Contributors:
-    .European Organization for Nuclear Research    (CERN) - initial API and implementation
-    .GSI Helmholtzzentrum für Schwerionenforschung (GSI)  - features and bugfixes
-*/
-
-
-#ifndef _PLC_SEND_ACTION_H_
-#define _PLC_SEND_ACTION_H_
-
-#include <silecs-communication/interface/core/PLCAction.h>
-
-namespace Silecs
-{
-class Action;
-class Block;
-class Context;
-
-/*!
- * \class PLCSendBlockMode
- * \brief Concrete action responsible to send a data block to the PLC (Block mode configuration)
- */
-class PLCSendBlockMode : public PLCAction
-{
-
-public:
-    PLCSendBlockMode(Block* block);
-    virtual ~PLCSendBlockMode();
-
-    /*!
-     * \fn execute
-     * Connect the PLC if necessary and send data-block to its memory.
-     * Overwrite the abstract method
-     * \return 0 if no error occurred. else return a error status from low level library
-     */
-    int execute(Context* pContext);
-
-private:
-
-};
-
-/*!
- * \class PLCSendDeviceMode
- * \brief Concrete action responsible to send a data block to the PLC (Device mode configuration)
- */
-class PLCSendDeviceMode : public PLCAction
-{
-
-public:
-    PLCSendDeviceMode(Block* block);
-    virtual ~PLCSendDeviceMode();
-
-    /*!
-     * \fn execute
-     * Connect the PLC if necessary and Get data-block to its memory.
-     * Overwrite the abstract method
-     * \return 0 if no error occurred. else return a error status from low level library
-     */
-    int execute(Context* pContext);
-
-private:
-
-};
-
-} // namespace
-
-#endif // _PLC_SEND_ACTION_H_
-
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCBlock.cpp b/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCBlock.cpp
index f705595..120255a 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCBlock.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCBlock.cpp
@@ -17,12 +17,12 @@ Contributors:
 #include <silecs-communication/interface/equipment/SilecsBlock.h>
 #include <silecs-communication/interface/equipment/SilecsRegister.h>
 #include <silecs-communication/interface/core/SilecsAction.h>
-#include <silecs-communication/interface/core/PLCRecvAction.h>
-#include <silecs-communication/interface/core/PLCSendAction.h>
 #include <silecs-communication/interface/utility/SilecsException.h>
 #include <silecs-communication/interface/utility/SilecsLog.h>
 #include <silecs-communication/interface/utility/StringUtilities.h>
 #include <silecs-communication/interface/equipment/PLCBlock.h>
+#include <silecs-communication/interface/core/Context.h>
+#include <silecs-communication/interface/communication/SilecsConnection.h>
 
 namespace Silecs
 {
@@ -32,7 +32,7 @@ PLCBlock::PLCBlock(PLC* thePLC, const ElementXML& blockNode, AccessType accessTy
 {
     // Create buffer for the block exchanges
     bufferSize_ = memSize_; //size of one block type (including alignements)
-    if (getPLC()->getProtocolModeID() == BlockMode)
+    if (thePLC_->getProtocolModeID() == BlockMode)
     { //BlockMode ==> access all devices in once
         unsigned int devicesWithBlock = getPLC()->getNrDevicesWithBlock(blockNode.getAttribute("name"));
         bufferSize_ *= devicesWithBlock;
@@ -48,38 +48,713 @@ PLCBlock::~PLCBlock()
     pBuffer_ = NULL;
 }
 
-InputBlock::InputBlock(PLC* thePLC, const ElementXML& blockNode, AccessType accessType) :
-                PLCBlock(thePLC, blockNode, accessType)
+int PLCBlock::recvBlockMode(Context* context)
 {
-    if (DEBUG & Log::topics_)
-        LOG(ALLOC) << "Block (create): " << name_ << ", plc: " << getPLC()->getName() << ", access: Input" << ", address: " << address_ << ", mem-size: " << memSize_ << ", buffer-size: " << bufferSize_;
+    timeval tod; //Time-of-day for register time-stamping
+    int errorCode = 0;
 
-    // Creates receive task which relies on the block exchange
-    if (getPLC()->getProtocolModeID() == BlockMode)
-        pAction_ = new PLCRecvBlockMode(this);
-    else
-        pAction_ = new PLCRecvDeviceMode(this);
+    Connection* pConn = thePLC_->getConnection();
+    AccessArea area = accessArea_;
+
+    try
+    {
+        //if transaction is for one device only, context contains its reference
+        if (context->isForOneDevice())
+        {
+            //Device context: get block of one device only ====================================
+            Device* pDev = context->getDevice();
+
+            //Base attributes of the device within the complete data block containing all devices.
+            unsigned long usedAddress = address_;
+            unsigned long usedSize = memSize_;
+            unsigned long usedDeviceOffset = pDev->getInputAddress(area) * usedSize;
+            unsigned char* pBuffer = ((unsigned char*)pBuffer_) + usedDeviceOffset;
+
+            // Overwrite device-block address, offset & size in case user wants to resize the block dynamically
+            if (withCustomAttributes() == true)
+            {
+                usedAddress = customAddress_;
+                usedSize = customSize_;
+                usedDeviceOffset = customOffset_;
+            }
+
+            if (RECV & Log::topics_)
+            {
+                if (pConn->isEnabled())
+                {
+                    Log(RECV).getLog() << "RecvAction (execute/ BlockMode): " << ", device: " << pDev->getLabel() << ", block: " << name_;
+                }
+            }
+            // Try to open the connection, if it fails. Throw an exception. On success all the
+            // PLC blocks will be updated this function will be called recursively within doOpen.
+            // Once all blocks are updated continue from here.
+            if (!pConn->doOpen(thePLC_))
+            {
+                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
+            }
+
+            //begin critical section
+            std::lock_guard<std::mutex> lock(bufferMux_);
+            switch (accessArea_)
+            {
+                case AccessArea::Digital:
+                    errorCode = pConn->readDIO(thePLC_, usedAddress, //Base address (or DBn) of the block
+                    usedDeviceOffset, //Device data address within the block
+                    usedSize, //Get one device-block only
+                    pBuffer //Buffer to store the data
+                    );
+                    break;
+                case AccessArea::Analog:
+                {
+                    errorCode = pConn->readAIO(thePLC_, usedAddress, //Base address (or DBn) of the block
+                    usedDeviceOffset, //Device data address within the block
+                    usedSize, //Get one device-block only
+                    pBuffer //Buffer to store the data
+                    );
+                    break;
+                }
+                case AccessArea::Memory:
+                default:
+                {
+                    errorCode = pConn->readMemory(thePLC_, usedAddress, //Base address (or DBn) of the block
+                    usedDeviceOffset, //Device data address within the block
+                    usedSize, //Get one device-block only
+                    pBuffer //Buffer to store the data
+                    );
+                    break;
+                }
+            }
+
+            if (pConn->isConnected() && (errorCode == 0))
+            { //Data have just been received: get time-of-day to time-stamp the registers
+                gettimeofday(&tod, 0);
+
+                //Device context: Extract all the device registers from that block
+                pDev->importRegisters(this, pBuffer, tod, context);
+
+                LOG_DELAY(RECV) << "done for block: " << name_;
+            }
+            //end critical section
+        }
+        else
+        {
+            //Dynamic resizing of the block is not possible in BLOCK_MODE
+            if (withCustomAttributes() == true)
+                throw SilecsException(__FILE__, __LINE__, COMM_BLOCK_RESIZING_NOT_ALLOWED);
+
+            deviceVectorType::iterator pDeviceIter;
+            deviceVectorType& deviceCol = thePLC_->getDeviceMap();
+
+            //Cluster/PLC context: get block of all devices in one go =========================
+            if (RECV & Log::topics_)
+            {
+                if (pConn->isEnabled())
+                {
+                    Log(RECV).getLog() << "RecvAction (execute/ BlockMode): " << name_ << ", " << deviceCol.size() << " device(s)";
+                }
+            }
+
+            // Try to open the connection, if it fails. Throw an exception. On success all the
+            // PLC blocks will be updated this function will be called recursively within doOpen.
+            // Once all blocks are updated continue from here.
+            if (!pConn->doOpen(thePLC_))
+            {
+                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
+            }
+
+            //begin critical section
+            std::lock_guard<std::mutex> lock(bufferMux_);
+            switch (area)
+            {
+                case AccessArea::Digital:
+                    errorCode = pConn->readDIO(thePLC_, address_, //Base address (or DBn) of the block
+                    0, //Get all devices from the first one
+                    bufferSize_, //Get blocks of all devices (full buffer size)
+                    (unsigned char*)pBuffer_ //Buffer to store the data (full buffer)
+                    );
+                    break;
+                case AccessArea::Analog:
+                {
+                    errorCode = pConn->readAIO(thePLC_, address_, //Base address (or DBn) of the block
+                    0, //Get all devices from the first one
+                    bufferSize_, //Get blocks of all devices (full buffer size)
+                    (unsigned char*)pBuffer_ //Buffer to store the data (full buffer)
+                    );
+                    break;
+                }
+                case AccessArea::Memory:
+                default:
+                {
+                    errorCode = pConn->readMemory(thePLC_, address_, //Base address (or DBn) of the block
+                    0, //Get all devices from the first one
+                    bufferSize_, //Get blocks of all devices (full buffer size)
+                    (unsigned char*)pBuffer_ //Buffer to store the data (full buffer)
+                    );
+                    break;
+                }
+            }
+
+            if (pConn->isConnected() && (errorCode == 0))
+            { //Data have just been received: get time-of-day to time-stamp the registers
+                gettimeofday(&tod, 0);
+
+                //PLC context: Extract all registers value of all devices of the PLC from that block
+                for (pDeviceIter = deviceCol.begin(); pDeviceIter != deviceCol.end(); ++pDeviceIter)
+                {
+                    Device* pDev = pDeviceIter->second;
+
+                    // Only extract if this device actually contains this block, otherwise skip it.
+                    if (!pDev->hasBlock(name_))
+                    {
+                        continue;
+                    }
+                    unsigned long deviceOffset = pDev->getInputAddress(area) * memSize_;
+                    unsigned char* pBuffer = ((unsigned char*)pBuffer_) + deviceOffset;
+                    pDev->importRegisters(this, pBuffer, tod, context);
+                }
+
+                LOG_DELAY(RECV) << "done for block: " << name_;
+            }
+        }
+    }
+    catch(const SilecsException& ex)
+    {
+        LOG(ERROR) << ex.what();
+        LOG(ERROR) << "RecvAction (execute/ BlockMode) for block " << name_ << " has failed.";
+        return ex.getCode();
+    }
+    return 0;
 }
 
-InputBlock::~InputBlock()
+int PLCBlock::recvDeviceMode(Context* context)
 {
+    timeval tod; //Time-of-day for register time-stamping
+    int errorCode = 0;
+    Connection* pConn = thePLC_->getConnection();
+
+    try
+    {
+        //if transaction is for one device only, context contains its reference
+        if (context->isForOneDevice())
+        {
+            //Device context: get block of one device only ====================================
+            Device* pDev = context->getDevice();
+
+            //Set base attributes of the device blocks
+            unsigned long usedDeviceAddress = pDev->getInputAddress(accessArea_);
+            unsigned long usedBlockAddress = address_;
+            unsigned long usedSize = memSize_;
+
+            // Overwrite device-block address, offset & size in case user wants to resize the block dynamically
+            if (withCustomAttributes() == true)
+            {
+                usedDeviceAddress = customAddress_;
+                usedBlockAddress = customOffset_;
+                usedSize = customSize_;
+            }
+
+            if (RECV & Log::topics_)
+            {
+                if (pConn->isEnabled())
+                {
+                    Log(RECV).getLog() << "RecvAction (execute/ DeviceMode): device: " << pDev->getLabel() << ", block: " << name_;
+                }
+            }
+
+            // Try to open the connection, if it fails. Throw an exception. On success all the
+            // PLC blocks will be updated this function will be called recursively within doOpen.
+            // Once all blocks are updated continue from here.
+            if (!pConn->doOpen(thePLC_))
+            {
+                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
+            }
+
+            //begin critical section
+            std::lock_guard<std::mutex> lock(bufferMux_);
+            switch (accessArea_)
+            {
+                case AccessArea::Digital:
+                    errorCode = pConn->readDIO(thePLC_, usedDeviceAddress, //Base address (or DBn) of the device
+                    usedBlockAddress, //Block data address within the device
+                    usedSize, //Get one device-block only
+                    (unsigned char*)pBuffer_ //Buffer to store the data
+                    );
+                    break;
+                case AccessArea::Analog:
+                {
+                    errorCode = pConn->readAIO(thePLC_, usedDeviceAddress, //Base address (or DBn) of the device
+                    usedBlockAddress, //Block data address within the device
+                    usedSize, //Get one device-block only
+                    (unsigned char*)pBuffer_ //Buffer to store the data
+                    );
+                    break;
+                }
+                case AccessArea::Memory:
+                default:
+                {
+                    errorCode = pConn->readMemory(thePLC_, usedDeviceAddress, //Base address (or DBn) of the device
+                    usedBlockAddress, //Block data address within the device
+                    usedSize, //Get one device-block only
+                    (unsigned char*)pBuffer_ //Buffer to store the data
+                    );
+                    break;
+                }
+            }
+
+            if (pConn->isConnected() && (errorCode == 0))
+            { //Data have just been received: get time-of-day to time-stamp the registers
+                gettimeofday(&tod, 0);
+
+                //Device context: Extract all the device registers from that block
+                pDev->importRegisters(this, (unsigned char*)pBuffer_, tod, context);
+
+                LOG_DELAY(RECV) << "done for block: " << name_;
+            }
+        }
+        else
+        {
+            //Dynamic resizing of the block is not possible in multiple access
+            if (withCustomAttributes() == true)
+                throw SilecsException(__FILE__, __LINE__, COMM_BLOCK_RESIZING_NOT_ALLOWED);
+
+            //Cluster/PLC context: get block of all devices one by one =========================
+            deviceVectorType::iterator pDeviceIter;
+            deviceVectorType& deviceCol = thePLC_->getDeviceMap();
+            for (pDeviceIter = deviceCol.begin(); pDeviceIter != deviceCol.end(); ++pDeviceIter)
+            {
+                Device* pDev = pDeviceIter->second;
+                if (!pDev->hasBlock(name_))
+                {
+                    continue;
+                }
+
+                if (RECV & Log::topics_)
+                {
+                    if (pConn->isEnabled())
+                    {
+                        Log(RECV).getLog() << "RecvAction (execute/ DeviceMode): device: " << pDev->getLabel() << ", block: " << name_;
+                    }
+                }
+
+                // Try to open the connection, if it fails. Throw an exception. On success all the
+                // PLC blocks will be updated this function will be called recursively within doOpen.
+                // Once all blocks are updated continue from here.
+                if (!pConn->doOpen(thePLC_))
+                {
+                    throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
+                }
+                //begin critical section
+                std::lock_guard<std::mutex> lock(bufferMux_);
+                switch (accessArea_)
+                {
+                    case AccessArea::Digital:
+                        errorCode = pConn->readDIO(thePLC_, pDev->getInputAddress(accessArea_), //Base address (or DBn) of the device
+                        address_, //Block data address within the device
+                        memSize_, //Get one device-block only
+                        (unsigned char*)pBuffer_ //Buffer to store the data
+                        );
+                        break;
+                    case AccessArea::Analog:
+                    {
+                        errorCode = pConn->readAIO(thePLC_, pDev->getInputAddress(accessArea_), //Base address (or DBn) of the device
+                        address_, //Block data address within the device
+                        memSize_, //Get one device-block only
+                        (unsigned char*)pBuffer_ //Buffer to store the data
+                        );
+                        break;
+                    }
+                    case AccessArea::Memory:
+                    default:
+                    {
+                        errorCode = pConn->readMemory(thePLC_, pDev->getInputAddress(accessArea_), //Base address (or DBn) of the device
+                        address_, //Block data address within the device
+                        memSize_, //Get one device-block only
+                        (unsigned char*)pBuffer_ //Buffer to store the data
+                        );
+                        break;
+                    }
+                }
+
+                if (pConn->isConnected() && (errorCode == 0))
+                { //Data have just been received: get time-of-day to time-stamp the registers
+                    gettimeofday(&tod, 0);
+
+                    //PLC context: Extract all registers value of the current device of the PLC from that block
+                    pDev->importRegisters(this, (unsigned char*)pBuffer_, tod, context);
+
+                    LOG_DELAY(RECV) << "done for block: " << name_;
+                }
+            }
+        }
+    }
+    catch(const SilecsException& ex)
+    {
+        LOG(ERROR) << ex.what();
+        LOG(ERROR) << "RecvAction (execute/ DeviceMode) failed.";
+        return ex.getCode();
+    }
+    return 0;
 }
 
-OutputBlock::OutputBlock(PLC* thePLC, const ElementXML& blockNode, AccessType accessType) :
-                PLCBlock(thePLC, blockNode, accessType)
+int PLCBlock::sendBlockMode(Context* context)
 {
-    if (DEBUG & Log::topics_)
-        LOG(ALLOC) << "Block (create): " << name_ << ", plc: " << getPLC()->getName() << ", access: Output" << ", address: " << address_ << ", mem-size: " << memSize_ << ", buffer-size: " << bufferSize_;
+    int errorCode = 0;
+    Connection* pConn = thePLC_->getConnection();
+
+    try
+    {
+        //if transaction is for one device only, context contains its reference
+        if (context->isForOneDevice())
+        {
+            //Device context: send block of one device only ====================================
+            Device* pDev = context->getDevice();
+
+            //Base attributes of the device within the complete data block containing all devices.
+            unsigned long usedAddress = address_;
+            unsigned long usedSize = memSize_;
+            AccessArea area = accessArea_;
+            unsigned long usedDeviceOffset = pDev->getOutputAddress(area) * usedSize;
+            unsigned char* pBuffer = ((unsigned char*)pBuffer_) + usedDeviceOffset;
+
+            // Overwrite device-block address, offset & size in case user wants to resize the block dynamically
+            if (withCustomAttributes() == true)
+            {
+                usedAddress = customAddress_;
+                usedSize = customSize_;
+                usedDeviceOffset = customOffset_;
+            }
 
-    // Creates send task which relies on the block exchange
-    if (getPLC()->getProtocolModeID() == BlockMode)
-        pAction_ = new PLCSendBlockMode(this);
+            // Try to open the connection, if it fails. Throw an exception. On success all the
+            // PLC blocks will be updated this function will be called recursively within doOpen.
+            // Once all blocks are updated continue from here.
+            if (!pConn->doOpen(thePLC_))
+            {
+                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
+            }
+            { //critical section
+                std::lock_guard<std::mutex> lock(bufferMux_);
+                if (pConn->isEnabled())
+                {
+                    LOG(SEND) << "SendAction (execute/ BlockMode): device: " << pDev->getLabel() << ", block: " << name_;
+
+                    //Device context: Export all the device registers to that block
+                    pDev->exportRegisters(this, pBuffer, context);
+                }
+
+                switch (area)
+                {
+                    case AccessArea::Digital:
+                        errorCode = pConn->writeDIO(thePLC_, usedAddress, //Base address (or DBn) of the block
+                            usedDeviceOffset, //Device data address within the block
+                            usedSize, //Set one device-block only
+                            pBuffer //Buffer which contain the data
+                            );
+                        break;
+                    case AccessArea::Analog:
+                    {
+                        errorCode = pConn->writeAIO(thePLC_, usedAddress, //Base address (or DBn) of the block
+                            usedDeviceOffset, //Device data address within the block
+                            usedSize, //Set one device-block only
+                            pBuffer //Buffer which contain the data
+                            );
+                        break;
+                    }
+                    case AccessArea::Memory:
+                    default:
+                        errorCode = pConn->writeMemory(thePLC_, usedAddress, //Base address (or DBn) of the block
+                            usedDeviceOffset, //Device data address within the block
+                            usedSize, //Set one device-block only
+                            pBuffer //Buffer which contain the data
+                            );
+                        break;
+                }
+            }
+
+            if (SEND & Log::topics_)
+            {
+                if (pConn->isEnabled())
+                {
+                    Log(SEND).getLogDelay() << "done for block: " << name_;
+                }
+            }
+        }
+        else
+        {
+            //Dynamic resizing of the block is not possible in BLOCK_MODE
+            if (withCustomAttributes() == true)
+                throw SilecsException(__FILE__, __LINE__, COMM_BLOCK_RESIZING_NOT_ALLOWED);
+
+            // Try to open the connection, if it fails. Throw an exception. On success all the
+            // PLC blocks will be updated this function will be called recursively within doOpen.
+            // Once all blocks are updated continue from here.
+            if (!pConn->doOpen(thePLC_))
+            {
+                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
+            }
+
+            { //critical section
+                std::lock_guard<std::mutex> lock(bufferMux_);
+                if (pConn->isEnabled())
+                {
+                    deviceVectorType::iterator pDeviceIter;
+                    deviceVectorType& deviceCol = thePLC_->getDeviceMap();
+
+                    //Cluster/PLC context: set block of all devices in one go ===========================
+                    LOG(SEND) << "SendAction (execute/ BlockMode): block: " << name_ << ", " << deviceCol.size() << " device(s)";
+
+                    //PLC context: Export all registers value of all devices of the PLC to that block
+                    for (pDeviceIter = deviceCol.begin(); pDeviceIter != deviceCol.end(); ++pDeviceIter)
+                    {
+                        Device* pDev = pDeviceIter->second;
+                        AccessArea area = accessArea_;
+                        unsigned long deviceOffset = pDev->getOutputAddress(area) * memSize_;
+                        unsigned char* pBuffer = ((unsigned char*)pBuffer_) + deviceOffset;
+                        pDev->exportRegisters(this, pBuffer, context);
+                    }
+                }
+
+                switch (accessArea_)
+                {
+                    case AccessArea::Digital:
+                        errorCode = pConn->writeDIO(thePLC_, address_, //Base address (or DBn) of the block
+                            0, //Set all devices from the first one
+                            bufferSize_, //Set blocks of all devices (full buffer size)
+                            (unsigned char*)pBuffer_ //Buffer which contain the data (full buffer)
+                            );
+                        break;
+                    case AccessArea::Analog:
+                    {
+                        errorCode = pConn->writeAIO(thePLC_, address_, //Base address (or DBn) of the block
+                            0, //Set all devices from the first one
+                            bufferSize_, //Set blocks of all devices (full buffer size)
+                            (unsigned char*)pBuffer_ //Buffer which contain the data (full buffer)
+                            );
+                        break;
+                    }
+                    case AccessArea::Memory:
+                    default:
+                        errorCode = pConn->writeMemory(thePLC_, address_, //Base address (or DBn) of the block
+                            0, //Set all devices from the first one
+                            bufferSize_, //Set blocks of all devices (full buffer size)
+                            (unsigned char*)pBuffer_ //Buffer which contain the data (full buffer)
+                            );
+                        break;
+                }
+            }
+
+            if (SEND & Log::topics_)
+            {
+                if (pConn->isEnabled())
+                {
+                    Log(SEND).getLogDelay() << "done for block: " << name_;
+                }
+            }
+        }
+    }
+    catch(const SilecsException& ex)
+    {
+        LOG(ERROR) << ex.what();
+        LOG(ERROR) << "SendAction (execute/ DeviceMode) on block: " << name_ << " has failed";
+        return ex.getCode();
+    }
+    return errorCode;
+}
+
+int PLCBlock::sendDeviceMode(Context* context)
+{
+    Connection* pConn = thePLC_->getConnection();
+    int errorCode = 0;
+    try
+    {
+        //if transaction is for one device only, context contains its reference
+        if (context->isForOneDevice())
+        {
+            //Device context: set block of one device only ====================================
+            Device* pDev = context->getDevice();
+
+            //Set base attributes of the device blocks
+            AccessArea area = accessArea_;
+            unsigned long usedDeviceAddress = pDev->getOutputAddress(area);
+            unsigned long usedBlockAddress = address_;
+            unsigned long usedSize = memSize_;
+
+            // Overwrite device-block address, offset & size in case user wants to resize the block dynamically
+            if (withCustomAttributes() == true)
+            {
+                usedDeviceAddress = customAddress_;
+                usedBlockAddress = customOffset_;
+                usedSize = customSize_;
+            }
+
+            // Try to open the connection, if it fails. Throw an exception. On success all the
+            // PLC blocks will be updated this function will be called recursively within doOpen.
+            // Once all blocks are updated continue from here.
+            if (!pConn->doOpen(thePLC_))
+            {
+                throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
+            }
+
+            { //critical section
+                std::lock_guard<std::mutex> lock(bufferMux_);
+                if (pConn->isEnabled())
+                {
+                    LOG(SEND) << "SendAction (execute/ DeviceMode): device: " << pDev->getLabel() << ", block: " << name_;
+
+                    //Device context: Export all the device registers to that block
+                    pDev->exportRegisters(this, (unsigned char*)pBuffer_, context);
+                }
+
+                switch (area)
+                {
+                    case AccessArea::Digital:
+                        errorCode = pConn->writeDIO(thePLC_, usedDeviceAddress, //Base address (or DBn) of the device
+                            usedBlockAddress, //Block data address within the device
+                            usedSize, //Set one device-block only
+                            (unsigned char*)pBuffer_ //Buffer which contain the data
+                            );
+                        break;
+                    case AccessArea::Analog:
+                    {
+                        errorCode = pConn->writeAIO(thePLC_, usedDeviceAddress, //Base address (or DBn) of the device
+                            usedBlockAddress, //Block data address within the device
+                            usedSize, //Set one device-block only
+                            (unsigned char*)pBuffer_ //Buffer which contain the data
+                            );
+                        break;
+                    }
+                    case AccessArea::Memory:
+                    default:
+                        errorCode = pConn->writeMemory(thePLC_, usedDeviceAddress, //Base address (or DBn) of the device
+                            usedBlockAddress, //Block data address within the device
+                            usedSize, //Set one device-block only
+                            (unsigned char*)pBuffer_ //Buffer which contain the data
+                            );
+                        break;
+                }
+            }
+
+            if (SEND & Log::topics_)
+            {
+                if (pConn->isEnabled())
+                {
+                    Log(SEND).getLogDelay() << "done for block: " << name_;
+                }
+            }
+        }
+        else
+        {
+            //Dynamic resizing of the block is not possible in multiple access
+            if (withCustomAttributes() == true)
+                throw SilecsException(__FILE__, __LINE__, COMM_BLOCK_RESIZING_NOT_ALLOWED);
+
+            //Cluster/PLC context: set block of all devices one by one =========================
+            deviceVectorType::iterator pDeviceIter;
+            deviceVectorType& deviceCol = thePLC_->getDeviceMap();
+            for (pDeviceIter = deviceCol.begin(); pDeviceIter != deviceCol.end(); ++pDeviceIter)
+            {
+                Device* pDev = pDeviceIter->second;
+                if (!pDev->hasBlock(name_))
+                {
+                    continue;
+                }
+                // Try to open the connection, if it fails. Throw an exception. On success all the
+                // PLC blocks will be updated this function will be called recursively within doOpen.
+                // Once all blocks are updated continue from here.
+                if (!pConn->doOpen(thePLC_))
+                {
+                    throw SilecsException{__FILE__, __LINE__, COMM_CONNECT_FAILURE};
+                }
+                { //critical section
+                    std::lock_guard<std::mutex> lock(bufferMux_);
+                    if (pConn->isEnabled())
+                    {
+                        LOG(SEND) << "SendAction (execute/ DeviceMode):  device: " << pDev->getLabel() << ", block: " << name_;
+
+                        //PLC context: Export all registers value of the current device of the PLC to that block
+                        pDev->exportRegisters(this, (unsigned char*)pBuffer_, context);
+                    }
+
+                    AccessArea area = accessArea_;
+                    switch (area)
+                    {
+                        case AccessArea::Digital:
+                            errorCode = pConn->writeDIO(thePLC_, pDev->getOutputAddress(area), //Base address (or DBn) of the device
+                                address_, //Block data address within the device
+                                memSize_, //Set one device-block only
+                                (unsigned char*)pBuffer_ //Buffer which contain the data
+                                );
+                            break;
+                        case AccessArea::Analog:
+                        {
+                            errorCode = pConn->writeAIO(thePLC_, pDev->getOutputAddress(area), //Base address (or DBn) of the device
+                                address_, //Block data address within the device
+                                memSize_, //Set one device-block only
+                                (unsigned char*)pBuffer_ //Buffer which contain the data
+                                );
+                            break;
+                        }
+                        case AccessArea::Memory:
+                        default:
+                            errorCode = pConn->writeMemory(thePLC_, pDev->getOutputAddress(area), //Base address (or DBn) of the device
+                                address_, //Block data address within the device
+                                memSize_, //Set one device-block only
+                                (unsigned char*)pBuffer_ //Buffer which contain the data
+                                );
+                            break;
+                    }
+                }
+
+                if (SEND & Log::topics_)
+                {
+                    if (pConn->isEnabled())
+                    {
+                        Log(SEND).getLogDelay() << "done for block: " << name_;
+                    }
+                }
+            }
+        }
+    }
+    catch(const SilecsException& ex)
+    {
+        LOG(ERROR) << ex.what();
+        LOG(ERROR) << "SendAction (execute/ DeviceMode) failed";
+        return ex.getCode();
+    }
+    return errorCode;
+}
+
+int PLCBlock::send(Context* context)
+{
+    if (accessType_ == AccessType::Acquisition)
+    {
+        throw SilecsException(__FILE__, __LINE__, DATA_WRITE_ACCESS_TYPE_MISMATCH);
+    }
+    
+    if (thePLC_->getProtocolModeID() == BlockMode)
+    {
+        return sendBlockMode(context);
+    }
     else
-        pAction_ = new PLCSendDeviceMode(this);
+    {
+        return sendDeviceMode(context);
+    }
 }
 
-OutputBlock::~OutputBlock()
+int PLCBlock::receive(Context* context)
 {
+    if (accessType_ == AccessType::Command)
+    {
+        throw SilecsException(__FILE__, __LINE__, DATA_READ_ACCESS_TYPE_MISMATCH);
+    }
+
+    if (thePLC_->getProtocolModeID() == BlockMode)
+    {
+        return recvBlockMode(context);
+    }
+    else
+    {
+        return recvDeviceMode(context);
+    }
 }
 
 }
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCBlock.h b/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCBlock.h
index fa7be69..ebf452b 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCBlock.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCBlock.h
@@ -20,50 +20,33 @@ Contributors:
 namespace Silecs
 {
 
+class Context;
+
 class PLCBlock : public Block
 {
 
 public:
     PLCBlock(PLC* thePLC, const ElementXML& blockNode, AccessType accessType);
-    ~PLCBlock();
+    virtual ~PLCBlock();
 
-    unsigned long getBufferSize() const
-    {
-        return bufferSize_;
-    }
+    int send(Context* context) override;
+    int receive(Context* context) override;
 
 protected:
     friend class Device;
 
+    int recvBlockMode(Context* context);
+    int recvDeviceMode(Context* context);
+    int sendBlockMode(Context* context);
+    int sendDeviceMode(Context* context);
+
     /// Send/Receive data size (size of block * nb of device)
     unsigned long bufferSize_;
 
-};
-
-/*! -----------------------------------------------------------------------
- * \class InputBlock
- * \brief This is a specific Block for Acquisition data (from PLC to client)
- */
-class InputBlock : public PLCBlock
-{
-
-public:
-    InputBlock(PLC* thePLC, const ElementXML& blockNode, AccessType accessType);
-    virtual ~InputBlock();
-
-};
-
-/*! -----------------------------------------------------------------------
- * \class OutputBlock
- * \brief This is a specific Block for Settings (from client to PLC)
- */
-class OutputBlock : public PLCBlock
-{
-
-public:
-    OutputBlock(PLC* thePLC, const ElementXML& blockNode, AccessType accessType);
-    virtual ~OutputBlock();
-
+private:
+    //The frame buffer update (import/export registers) and related read/write data must be done
+    //in a critical section (the same buffer is used to send/recv data to all devices).
+    std::mutex bufferMux_;
 };
 
 } // namespace
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsBlock.cpp b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsBlock.cpp
index df65ac0..10e521c 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsBlock.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsBlock.cpp
@@ -43,9 +43,6 @@ Block::Block(PLC* thePLC, const ElementXML& blockNode, AccessType accessType) :
 
     resetCustomAttributes(); //by default custom-size of the block is the maximum size
 
-    /*In case of InOut block we will instantiate 2 different blocks (input/output)
-     * and the accessType is given by the caller.
-     */
     accessType_ = accessType;
 }
 
@@ -53,9 +50,6 @@ Block::~Block()
 {
     if (DEBUG & Log::topics_)
         LOG(ALLOC) << "Block (delete): " << name_;
-
-    if (pAction_ != NULL)
-        delete pAction_;
 }
 
 AccessType Block::whichAccessType(std::string type)
@@ -134,11 +128,6 @@ void Block::setCustomAttributes(const unsigned long customAddress, const unsigne
     LOG(DEBUG) << "Set custom attributes of block: " << name_ << ", memSize: " << memSize_ << ", customSize: " << customSize << ", customAddress: " << customAddress << ", customOffset: " << customOffset;
 }
 
-Action* Block::getAction() const
-{
-    return pAction_;
-}
-
 void Block::resetCustomAttributes()
 {
     customAttributes_ = false;
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsBlock.h b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsBlock.h
index 1f89ee8..722a74a 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsBlock.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsBlock.h
@@ -38,6 +38,9 @@ public:
     Block(PLC* thePLC, const ElementXML& blockNode, AccessType accessType);
     virtual ~Block();
 
+    virtual int receive(Context* context) = 0;
+    virtual int send(Context* context) = 0;
+
     /*!
      * \fn whichAccessType
      * \return the enumeration value of the given access-type string
@@ -80,20 +83,6 @@ public:
     {
         return (accessArea_);
     }
-    unsigned long getAddress() const
-    {
-        return address_;
-    }
-    unsigned long getMemSize() const
-    {
-        return memSize_;
-    }
-    void* getBuffer()
-    {
-        return pBuffer_;
-    }
-
-    Action* getAction() const;
 
     /// @cond
     void setCustomAttributes(const unsigned long customAddress, const unsigned long customOffset, const unsigned long customSize);
@@ -102,18 +91,6 @@ public:
     {
         return customAttributes_;
     }
-    unsigned long getCustomAddress() const
-    {
-        return customAddress_;
-    }
-    unsigned long getCustomOffset() const
-    {
-        return customOffset_;
-    }
-    unsigned long getCustomSize() const
-    {
-        return customSize_;
-    }
     /// @endcond
 
 protected:
@@ -153,9 +130,6 @@ protected:
 
     unsigned long address_;
 
-    /// Related PLC task to this data-block
-    Action* pAction_;
-
     // Send/Receive data buffer
     void *pBuffer_;
 };
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsCluster.cpp b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsCluster.cpp
index e9a9884..2ec9ed1 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsCluster.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsCluster.cpp
@@ -97,9 +97,9 @@ bool Cluster::getAddressByHostName(const std::string hostName, std::string& IPAd
     return false;
 }
 
-Block* Cluster::getBlock(const std::string blockName, AccessType accessType)
+Block* Cluster::getBlock(const std::string blockName)
 { // Retrieve the block from the first PLC (same for each of them)
-    return plcMap_.begin()->second->getBlock(blockName, accessType);
+    return plcMap_.begin()->second->getBlock(blockName);
 }
 
 std::string Cluster::getBlockList(AccessType accessType)
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsCluster.h b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsCluster.h
index aaf3d6d..aa68187 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsCluster.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsCluster.h
@@ -142,9 +142,9 @@ private:
 
     /*!
      * \fn getBlock
-     * \brief returns one instance of the requested block checking its access-type
+     * \brief returns instance of the requested block
      */
-    Block* getBlock(const std::string blockName, AccessType accessType = AccessType::Setting);
+    Block* getBlock(const std::string blockName);
 
     /// Store the current FEC name
     std::string hostName_;
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsDevice.cpp b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsDevice.cpp
index de342e1..d324d6d 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsDevice.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsDevice.cpp
@@ -119,7 +119,7 @@ int Device::recv(const std::string& blockName)
     Context context(this, false);
 
     //Execute the receive action for the required device (from the current thread)
-    return getBlock(blockName, AccessType::Acquisition)->getAction()->execute(&context);
+    return getBlock(blockName)->receive(&context);
 }
 
 int Device::send(const std::string& blockName)
@@ -132,7 +132,7 @@ int Device::send(const std::string& blockName)
     Context context(this, false);
 
     //Execute the receive action for the required device (from the current thread)
-    return getBlock(blockName, AccessType::Command)->getAction()->execute(&context);
+    return getBlock(blockName)->send(&context);
 }
 
 std::string Device::getLabel() const
@@ -203,7 +203,7 @@ void Device::instantiateRegisters(const std::string& blockName, AccessType acces
     blockRegisterMap_.insert(std::make_pair(blockName, registerCol));
 }
 
-Block* Device::getBlock(const std::string& blockName, AccessType accessType)
+Block* Device::getBlock(const std::string& blockName)
 {
     if (!hasBlock(blockName))
     {
@@ -211,7 +211,7 @@ Block* Device::getBlock(const std::string& blockName, AccessType accessType)
         errorMessage << "Block not found! The block '" << blockName << "' does not exist on the device '" << label_ << "'.";
         throw SilecsException(__FILE__, __LINE__, errorMessage.str());
     }
-    return thePLC_->getBlock(blockName, accessType);
+    return thePLC_->getBlock(blockName);
 }
 
 bool Device::hasBlock(const std::string& blockName)
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsDevice.h b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsDevice.h
index 4c7d6c0..2eb7916 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsDevice.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsDevice.h
@@ -107,10 +107,7 @@ private:
     friend class PLC;
     friend class Register;
     friend class PLCRegister;
-    friend class PLCRecvBlockMode;
-    friend class PLCRecvDeviceMode;
-    friend class PLCSendBlockMode;
-    friend class PLCSendDeviceMode;
+    friend class PLCBlock;
     friend class Context;
 
     friend class CNVRegister;
@@ -148,7 +145,7 @@ private:
      * \fn getBlock
      * \brief returns one instance of the requested block checking its access-type
      */
-    Block* getBlock(const std::string& blockName, AccessType accessType = AccessType::Setting);
+    Block* getBlock(const std::string& blockName);
 
     bool hasBlock(const std::string& blockName);
 
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.cpp b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.cpp
index 6419f53..b8e8a98 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.cpp
@@ -377,26 +377,26 @@ int PLC::recv(const std::string& blockName)
 {
     //Synchronous data receive
     //Execute the receive action for all the PLC devices (from the current thread)
-    return getBlock(blockName, AccessType::Acquisition)->getAction()->execute(allDevicesTransaction_);
+    return getBlock(blockName)->receive(allDevicesTransaction_);
 }
 
 int PLC::send(const std::string& blockName)
 {
     //Synchronous data send
     //Execute the send action for all the PLC devices (from the current thread)
-    return getBlock(blockName, AccessType::Command)->getAction()->execute(allDevicesTransaction_);
+    return getBlock(blockName)->send(allDevicesTransaction_);
 }
 
 std::future<int> PLC::recvAsync(const std::string& blockName)
 {
     //Schedule task using asynchronous call
-    return std::async(std::launch::async, &Action::execute, getBlock(blockName, AccessType::Acquisition)->getAction(), allDevicesTransaction_);
+    return std::async(std::launch::async, &Block::receive, getBlock(blockName), allDevicesTransaction_);
 }
 
 std::future<int> PLC::sendAsync(const std::string& blockName)
 {
     //Schedule task using asynchronous call
-    return std::async(std::launch::async, &Action::execute, getBlock(blockName, AccessType::Command)->getAction(), allDevicesTransaction_);
+    return std::async(std::launch::async, &Block::send, getBlock(blockName), allDevicesTransaction_);
 }
 
 void PLC::extractDatabase()
@@ -490,36 +490,18 @@ void PLC::extractDatabase()
             AccessType accessType = Block::whichAccessType( (*blockIter).getName());
             LOG((DIAG)) << "The block '" << blockName << " of type '" << (*blockIter).getName() << "' will be created.";
 
-            Block* pBlock = 0;
-            // Instantiate Input blocks ------------------------------------------------------
-            if (accessType == AccessType::Acquisition || accessType == AccessType::Setting)
-            { //Instantiate the block, forcing it to Acquisition access for Setting case
-
-                if (brandID_ == Ni)
+            if (brandID_ == Ni)
+            {
 #ifdef NI_SUPPORT_ENABLED
-                    pBlock = new CNVInputBlock(this, (*pBlockElCol)[i], accessType);
+                // TODO: Implement blocks in the same way as done for PLCBlock.
+                throw SilecsException(__FILE__, __LINE__, "CNV Blocks not implemented");
 #else
-                    throw SilecsException(__FILE__, __LINE__, "Support for NI-Devices is disabled");
+                throw SilecsException(__FILE__, __LINE__, "Support for NI-Devices is disabled");
 #endif
-                else
-                    pBlock = new InputBlock(this, *blockIter, AccessType::Acquisition);
-                blockCol_.push_back(pBlock); // FIXME: Currently a block is instatiated twice if it is READ+WRITE
-
             }
-            // Instantiate Output blocks ------------------------------------------------------
-            if (accessType == AccessType::Command || accessType == AccessType::Setting)
-            { //Instantiate the block, forcing it to Command access for Setting case
 
-                if (brandID_ == Ni)
-#ifdef NI_SUPPORT_ENABLED
-                    pBlock = new CNVOutputBlock(this, (*pBlockElCol)[i], accessType);
-#else
-                    throw SilecsException(__FILE__, __LINE__, "Support for NI-Devices is disabled");
-#endif
-                else
-                    pBlock = new OutputBlock(this, *blockIter, AccessType::Command);
-                blockCol_.push_back(pBlock); // FIXME: Currently a block is instatiated twice if it is READ+WRITE
-            }
+            Block* pBlock = new PLCBlock(this, *blockIter, accessType);
+            blockCol_.push_back(pBlock);
 
             //Force BusController type (with SilecsHeader check), if there is at least one Memory block defined
             if (pBlock->getAccessArea() == AccessArea::Memory)
@@ -574,15 +556,15 @@ Connection* PLC::getConnection()
     return plcConn_;
 }
 
-Block* PLC::getBlock(const std::string& blockName, AccessType accessType) const
+Block* PLC::getBlock(const std::string& blockName) const
 {
     for (auto block = blockCol_.begin(); block != blockCol_.end(); block++)
     {
-        if ( (*block)->getName() == blockName && (*block)->getAccessType() == accessType)
+        if ( (*block)->getName() == blockName)
             return *block;
     }
     std::ostringstream error;
-    error << "The block '" << blockName << "' was not found for the access type: '" << Block::whichAccessType(accessType) << "'";
+    error << "The block '" << blockName << "' was not found.";
     throw SilecsException(__FILE__, __LINE__, error.str());
 }
 
@@ -723,24 +705,24 @@ void PLC::setReadBlockAttributes(const std::string& blockName, const unsigned lo
 {
     //Set the custom offset/size of the referred Input block if any.
     //Will throw an exception if that block-name does not exist.
-    getBlock(blockName, AccessType::Acquisition)->setCustomAttributes(customAddress, customOffset, customSize);
+    getBlock(blockName)->setCustomAttributes(customAddress, customOffset, customSize);
 }
 
 void PLC::resetReadBlockAttributes(const std::string& blockName)
 {
-    getBlock(blockName, AccessType::Acquisition)->resetCustomAttributes();
+    getBlock(blockName)->resetCustomAttributes();
 }
 
 void PLC::setWriteBlockAttributes(const std::string& blockName, const unsigned long customAddress, const unsigned long customOffset, const unsigned long customSize)
 {
     //Set the custom address/offset/size of the referred Output block if any.
     //Will throw an exception if that block-name does not exist.
-    getBlock(blockName, AccessType::Command)->setCustomAttributes(customAddress, customOffset, customSize);
+    getBlock(blockName)->setCustomAttributes(customAddress, customOffset, customSize);
 }
 
 void PLC::resetWriteBlockAttributes(const std::string& blockName)
 {
-    getBlock(blockName, AccessType::Command)->resetCustomAttributes();
+    getBlock(blockName)->resetCustomAttributes();
 }
 
 } // namespace
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.h b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.h
index 3ba2a76..062606c 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.h
@@ -297,9 +297,9 @@ public:
 
     /*!
      * \fn getBlock
-     * \brief returns one instance of the requested block checking its access-type
+     * \brief returns instance of the requested block
      */
-    Block* getBlock(const std::string& blockName, AccessType accessType) const;
+    Block* getBlock(const std::string& blockName) const;
 
     /*!
      * \brief Used to Enable the Client/PLC connection and connect the PLC immediately if needed (connectNow=true).
-- 
GitLab