From d5c644fd294f5f98f474a8e4afb10d60ab8413e4 Mon Sep 17 00:00:00 2001
From: kochalas <konstantinos.chalas@cern.ch>
Date: Fri, 8 Sep 2017 12:16:22 +0200
Subject: [PATCH] [SIL-341] Implement byte addressing support for Modbus TCP

---
 .../interface/communication/CNVConnection.cpp | 214 ++++----
 .../interface/communication/CNVConnection.h   | 148 ++---
 .../interface/communication/MBConnection.cpp  | 512 ++++++++++++------
 .../interface/communication/MBConnection.h    |  73 ++-
 .../communication/SNAP7Connection.cpp         |  17 +
 .../interface/communication/SNAP7Connection.h |   8 +-
 .../communication/SilecsConnection.h          |   9 +-
 .../interface/core/PLCRecvAction.cpp          |  34 +-
 .../interface/core/PLCSendAction.cpp          |  32 +-
 9 files changed, 674 insertions(+), 373 deletions(-)

diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/CNVConnection.cpp b/silecs-communication-cpp/src/silecs-communication/interface/communication/CNVConnection.cpp
index dd6ebf5..77ae841 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/communication/CNVConnection.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/CNVConnection.cpp
@@ -14,7 +14,6 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #ifdef NI_SUPPORT_ENABLED
-
 #include <silecs-communication/interface/communication/CNVConnection.h>
 
 #include <silecs-communication/interface/communication/SilecsConnection.h>
@@ -28,134 +27,155 @@
 namespace Silecs
 {
 
-    CNVConnection::CNVConnection(PLC* thePLC) : Connection(thePLC)
-    {
-        LOG(ALLOC) << "CNVConnection (create): " << thePLC->getName();
-    }
+CNVConnection::CNVConnection(PLC* thePLC) :
+                Connection(thePLC)
+{
+    LOG(ALLOC) << "CNVConnection (create): " << thePLC->getName();
+}
 
-    CNVConnection::~CNVConnection()
-    {
-        //Close the connection before removing resources
-        //disable(); must be done before removing resource
-    }
+CNVConnection::~CNVConnection()
+{
+    //Close the connection before removing resources
+    //disable(); must be done before removing resource
+}
 
-    /* Check for errors */
-    int CNVConnection::errChk(PLC* thePLC, int code) // throw (std::string*)
+/* Check for errors */
+int CNVConnection::errChk(PLC* thePLC, int code) // throw (std::string*)
+{
+    if (code != 0)
     {
-        if(code != 0)
-        {   LOG(DEBUG) << "CNV error occurred: " << code << ": " << CNVGetErrorDescription(code);
-            //Do not close the connection if it's only for data missing (when deleting empty Data object)
-            if (code != CNVInvalidDataHandleError)
-            {
-                LOG(COMM) << "Transaction failure with PXI: " << thePLC->getName() << ". CNV[" << code << "]: " << CNVGetErrorDescription(code);
-                doClose(thePLC, /*withLock =*/true);
-                throw SilecsException(__FILE__, __LINE__, CNV_INTERNAL_ERROR, std::string(CNVGetErrorDescription(code))+" in CNVConnection");
-            }
+        LOG(DEBUG) << "CNV error occurred: " << code << ": " << CNVGetErrorDescription(code);
+        //Do not close the connection if it's only for data missing (when deleting empty Data object)
+        if (code != CNVInvalidDataHandleError)
+        {
+            LOG(COMM) << "Transaction failure with PXI: " << thePLC->getName() << ". CNV[" << code << "]: " << CNVGetErrorDescription(code);
+            doClose(thePLC, /*withLock =*/true);
+            throw SilecsException(__FILE__, __LINE__, CNV_INTERNAL_ERROR, std::string(CNVGetErrorDescription(code)) + " in CNVConnection");
         }
-        return code;
     }
+    return code;
+}
 
-    /* Read Data */
-    int CNVConnection::readData(PLC* thePLC, CNVBufferedSubscriber *handle,CNVData *data)
-    {
-        CNVBufferDataStatus status;
+/* Read Data */
+int CNVConnection::readData(PLC* thePLC, CNVBufferedSubscriber *handle, CNVData *data)
+{
+    CNVBufferDataStatus status;
 
-        /* Buffer is used only for performance reason.
-         * Always return the last value in the buffer discarding the oldest ones.
-         */
+    /* Buffer is used only for performance reason.
+     * Always return the last value in the buffer discarding the oldest ones.
+     */
 
-        //read the oldest data in the buffer
-        int errorCode = errChk(thePLC, CNVGetDataFromBuffer(*handle, data, &status));
+    //read the oldest data in the buffer
+    int errorCode = errChk(thePLC, CNVGetDataFromBuffer(*handle, data, &status));
 
-        if ((errorCode == 0) && (status != CNVStaleData))
+    if ( (errorCode == 0) && (status != CNVStaleData))
+    {
+        if (status != CNVNoData)
+        { //buffer was not empty, try to read again to get the very last data if any
+            errChk(thePLC, CNVDisposeData(*data)); //CNV requires to free the buffer on every CNVGetDataFromBuffer
+            errorCode = errChk(thePLC, CNVGetDataFromBuffer(*handle, data, &status));
+        }
+        else
         {
-            if (status != CNVNoData)
-            { //buffer was not empty, try to read again to get the very last data if any
-                errChk(thePLC, CNVDisposeData(*data));//CNV requires to free the buffer on every CNVGetDataFromBuffer
-                errorCode = errChk(thePLC, CNVGetDataFromBuffer(*handle, data, &status));
-            }
-            else
-            {   LOG(DEBUG) << "CNVConnection::readData() : buffer is empty (variables may not be initialized properly)";
-                errorCode = CNVEmptyDataError;
-            }
+            LOG(DEBUG) << "CNVConnection::readData() : buffer is empty (variables may not be initialized properly)";
+            errorCode = CNVEmptyDataError;
         }
-        return errorCode;
     }
+    return errorCode;
+}
 
-    /* Write Data */
-    int CNVConnection::writeData(PLC* thePLC, const char *networkVariablePathname, CNVData data)
-    {
-        CNVWriter writer;
-
-        int errorCode = errChk(thePLC, CNVCreateWriter(networkVariablePathname , NULL, NULL, CNVWaitForever , 0, &writer));
+/* Write Data */
+int CNVConnection::writeData(PLC* thePLC, const char *networkVariablePathname, CNVData data)
+{
+    CNVWriter writer;
 
-        if (errorCode ==0)
-        {   CNVWrite (writer, data, 0);
-            CNVDispose(writer);
-        }
-        return errorCode;
-    }
+    int errorCode = errChk(thePLC, CNVCreateWriter(networkVariablePathname, NULL, NULL, CNVWaitForever, 0, &writer));
 
-    int CNVConnection::readMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
-    {
-        return readData(thePLC, address, offset, size, pBuffer);
-    }
-    int CNVConnection::writeMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+    if (errorCode == 0)
     {
-        return writeData(thePLC, address, offset, size, pBuffer);
+        CNVWrite(writer, data, 0);
+        CNVDispose(writer);
     }
-    int CNVConnection::readIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
-    {
+    return errorCode;
+}
 
-        LOG(ERROR) << "Read IO not supported for CNV";
-        return 0;
-    }
+int CNVConnection::readMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+{
+    return readData(thePLC, address, offset, size, pBuffer);
+}
+int CNVConnection::writeMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+{
+    return writeData(thePLC, address, offset, size, pBuffer);
+}
+int CNVConnection::readAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+{
 
-    int CNVConnection::writeIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
-    {
-        LOG(ERROR) << "Write IO not supported for CNV";
-        return 0;
-    }
+    LOG(ERROR) << "Read IO not supported for CNV";
+    return 0;
+}
 
-    bool CNVConnection::open(PLC* thePLC)
-    {
-        //Controller is reachable at this stage (ping done from doOpen)
-        bool isOK = true;//all subscription is fine a priori
+int CNVConnection::writeAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+{
+    LOG(ERROR) << "Write IO not supported for CNV";
+    return 0;
+}
 
-        LOG(DEBUG) << "CNVConnection::open(): Subscribe to all input variables";
+int CNVConnection::readDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+{
+
+    LOG(ERROR) << "Read IO not supported for CNV";
+    return 0;
+}
 
-        // Create all the subscriptions
-        blockMapType::iterator pBlockIter;
-        for(pBlockIter = thePLC->getBlockMap().begin(); pBlockIter != thePLC->getBlockMap().end(); ++pBlockIter)
+int CNVConnection::writeDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+{
+    LOG(ERROR) << "Write IO not supported for CNV";
+    return 0;
+}
+
+bool CNVConnection::open(PLC* thePLC)
+{
+    //Controller is reachable at this stage (ping done from doOpen)
+    bool isOK = true; //all subscription is fine a priori
+
+    LOG(DEBUG) << "CNVConnection::open(): Subscribe to all input variables";
+
+    // Create all the subscriptions
+    blockMapType::iterator pBlockIter;
+    for (pBlockIter = thePLC->getBlockMap().begin(); pBlockIter != thePLC->getBlockMap().end(); ++pBlockIter)
+    {
+        //Attention: only CNVInputBlock has doSubscribe/unSubscribe feature (key-map can be used to select appropriate object)
+        if (pBlockIter->first.find(Block::whichAccessType(Input)) != std::string::npos)
         {
-            //Attention: only CNVInputBlock has doSubscribe/unSubscribe feature (key-map can be used to select appropriate object)
-            if (pBlockIter->first.find(Block::whichAccessType(Input)) != std::string::npos)
-            {   if (static_cast<CNVInputBlock*>(pBlockIter->second)->doSubscribe() == false)
-                {   isOK = false;
-                    break;
-                }
+            if (static_cast<CNVInputBlock*>(pBlockIter->second)->doSubscribe() == false)
+            {
+                isOK = false;
+                break;
             }
         }
-        return isOK;
     }
+    return isOK;
+}
 
-    bool CNVConnection::close(PLC* thePLC)
-    {
-        LOG(DEBUG) << "CNVConnection::close(): unSubscribe all input variables";
+bool CNVConnection::close(PLC* thePLC)
+{
+    LOG(DEBUG) << "CNVConnection::close(): unSubscribe all input variables";
 
-        // Delete all the subscription
-        blockMapType::iterator pBlockIter;
-        for(pBlockIter = thePLC->getBlockMap().begin(); pBlockIter != thePLC->getBlockMap().end(); ++pBlockIter)
+    // Delete all the subscription
+    blockMapType::iterator pBlockIter;
+    for (pBlockIter = thePLC->getBlockMap().begin(); pBlockIter != thePLC->getBlockMap().end(); ++pBlockIter)
+    {
+        //Attention: only CNVInputBlock has doSubscribe/unSubscribe feature (key-map can be used to select appropriate object)
+        if (pBlockIter->first.find(Block::whichAccessType(Input)) != std::string::npos)
         {
-            //Attention: only CNVInputBlock has doSubscribe/unSubscribe feature (key-map can be used to select appropriate object)
-            if (pBlockIter->first.find(Block::whichAccessType(Input)) != std::string::npos)
-            {   static_cast<CNVInputBlock*>(pBlockIter->second)->unSubscribe();
-            }
+            static_cast<CNVInputBlock*>(pBlockIter->second)->unSubscribe();
         }
-        isConnected_ = false;
-        return true;
     }
+    isConnected_ = false;
+    return true;
+}
 
 } // namespace
 
+
 #endif //NI_SUPPORT_ENABLED
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/CNVConnection.h b/silecs-communication-cpp/src/silecs-communication/interface/communication/CNVConnection.h
index dc9d002..ed5c1d1 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/communication/CNVConnection.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/CNVConnection.h
@@ -35,82 +35,86 @@ extern "C"
 
 namespace Silecs
 {
+/*!
+ * \class CNVConnection
+ * \brief CNV-communication object for PXI Shared Variable Protocol
+ */
+class CNVConnection : public Connection
+{
+
+public:
+    CNVConnection(PLC* thePLC);
+    virtual ~CNVConnection();
+
+    /*!
+     * \brief Read Data from the specified buffer handler
+     * \return the read CNVData and error code (0 = everything is ok)
+     */
+    //CNVData readData(CNVBufferedSubscriber *handle);
+    int readData(PLC* thePLC, CNVBufferedSubscriber *handle, CNVData *data);
+
     /*!
-     * \class CNVConnection
-     * \brief CNV-communication object for PXI Shared Variable Protocol
+     * \brief Write the specified variable, data must be already in CNVData format.
+     * In case of error raises a std::string* exception containing the error message
+     * \return error code (0 = everything is ok)
      */
-    class CNVConnection : public Connection
+    int writeData(PLC* thePLC, const char *networkVariablePathname, CNVData data);
+
+    /*!
+     * \brief Subscribes for changes on the the specified variable.
+     * In case of error raises a std::string* exception containing the error message
+     */
+    void monitorData(const char *networkVariablePathname);
+
+    // not implemented. here because of virtual in super class
+    // TODO: review in order to get read of it
+    int readData(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* buffer)
+    {
+        return -1;
+    }
+    ;
+
+    // not implemented. here because of virtual in super class
+    // TODO: review in order to get read of it
+    int writeData(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* buffer)
+    {
+        return -1;
+    }
+    ;
+    int readMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int writeMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int readAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int writeAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int readDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int writeDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+
+private:
+    // Subscriber
+    CNVSubscriber subscriber;
+
+    /*!
+     * \brief Print the error message related to the code and throw exception if needed
+     * \return the error code itself (0 everything is ok)
+     */
+    int errChk(PLC* thePLC, int code);
+
+    /*!
+     * \brief Open the connection
+     * It actually return a boolean which indicates if the server responded to a ping operation
+     */
+    bool open(PLC* thePLC);
+
+    /*!
+     * \brief Close the connection with the server
+     */
+    bool close(PLC* thePLC);
+
+    bool checkError(PLC* thePLC, int err, bool retry)
     {
+        return false;
+    }
 
-    public:
-        CNVConnection(PLC* thePLC);
-        virtual ~CNVConnection();
-
-        /*!
-         * \brief Read Data from the specified buffer handler
-         * \return the read CNVData and error code (0 = everything is ok)
-         */
-        //CNVData readData(CNVBufferedSubscriber *handle);
-        int readData(PLC* thePLC, CNVBufferedSubscriber *handle,CNVData *data);
-
-        /*!
-         * \brief Write the specified variable, data must be already in CNVData format.
-         * In case of error raises a std::string* exception containing the error message
-         * \return error code (0 = everything is ok)
-         */
-        int writeData(PLC* thePLC, const char *networkVariablePathname, CNVData data);
-
-        /*!
-         * \brief Subscribes for changes on the the specified variable.
-         * In case of error raises a std::string* exception containing the error message
-         */
-        void monitorData(const char *networkVariablePathname);
-
-        // not implemented. here because of virtual in super class
-        // TODO: review in order to get read of it
-        int readData(PLC* thePLC, long address,
-                unsigned long offset,
-                unsigned long size,
-                unsigned char* buffer)
-        {   return -1;};
-
-        // not implemented. here because of virtual in super class
-        // TODO: review in order to get read of it
-        int writeData(PLC* thePLC, long address,
-                unsigned long offset,
-                unsigned long size,
-                unsigned char* buffer)
-        {   return -1;};
-        int readMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-        int writeMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-        int readIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-        int writeIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-
-    private:
-        // Subscriber
-        CNVSubscriber subscriber;
-
-        /*!
-         * \brief Print the error message related to the code and throw exception if needed
-         * \return the error code itself (0 everything is ok)
-         */
-        int errChk(PLC* thePLC, int code);
-
-        /*!
-         * \brief Open the connection
-         * It actually return a boolean which indicates if the server responded to a ping operation
-         */
-        bool open(PLC* thePLC);
-
-        /*!
-         * \brief Close the connection with the server
-         */
-        bool close(PLC* thePLC);
-
-        bool checkError(PLC* thePLC, int err, bool retry)
-        {   return false;}
-
-    };
+};
 
 } // namespace
 
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.cpp b/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.cpp
index 4b6ca5f..414827e 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.cpp
@@ -20,234 +20,424 @@
 #include <silecs-communication/interface/communication/MBConnection.h>
 #include <silecs-communication/interface/utility/SilecsException.h>
 
-//Modbus max data size definition (byte counting)
-#define MAX_WRITE_DATA_SIZE MODBUS_MAX_WRITE_REGISTERS*2
-#define MAX_READ_DATA_SIZE  MODBUS_MAX_READ_REGISTERS*2
+#include <bitset>
+
+// Modbus max data size definition (byte counting)
+#define MAX_WRITE_DATA_SIZE MODBUS_MAX_WRITE_REGISTERS * 2
+#define MAX_READ_DATA_SIZE MODBUS_MAX_READ_REGISTERS * 2
 
 namespace Silecs
 {
 
-    MBConnection::MBConnection(PLC* thePLC) :
-    Connection(thePLC)
-    {
-        LOG(ALLOC) << "MBConnection (create): " << thePLC->getName();
+MBConnection::MBConnection(PLC *thePLC) :
+                Connection(thePLC)
+{
+    LOG(ALLOC) << "MBConnection (create): " << thePLC->getName();
 
-        //Connection use IP address to limit the naming-server accesses
-        readCtx_ = modbus_new_tcp((char *)thePLC->getIPAddress().c_str(), MODBUS_TCP_DEFAULT_PORT /*=502*/);
-        writeCtx_ = modbus_new_tcp((char *)thePLC->getIPAddress().c_str(), MODBUS_TCP_DEFAULT_PORT /*=502*/);
+    // Connection use IP address to limit the naming-server accesses
+    readCtx_ = modbus_new_tcp((char *)thePLC->getIPAddress().c_str(), MODBUS_TCP_DEFAULT_PORT /*=502*/);
+    writeCtx_ = modbus_new_tcp((char *)thePLC->getIPAddress().c_str(), MODBUS_TCP_DEFAULT_PORT /*=502*/);
 
-        /*TODO: To be adjusted with the next stable libmodbus release (>3.1.1)
-         which will fix the current timeout response time issue (see libmodbus forum).
+    /*TODO: To be adjusted with the next stable libmodbus release (>3.1.1)
+     which will fix the current timeout response time issue (see libmodbus
+     forum).
 
-         Define Modbus response timeout
-         struct timeval response_timeout;
-         response_timeout.tv_sec = 0;
-         response_timeout.tv_usec = 10000;
+     Define Modbus response timeout
+     struct timeval response_timeout;
+     response_timeout.tv_sec = 0;
+     response_timeout.tv_usec = 10000;
 
-         modbus_set_response_timeout(readCtx_ , &response_timeout);
-         modbus_set_response_timeout(writeCtx_ , &response_timeout);
-         */
+     modbus_set_response_timeout(readCtx_ , &response_timeout);
+     modbus_set_response_timeout(writeCtx_ , &response_timeout);
+     */
 
-        modbus_set_slave(readCtx_, 1);
-        //modbus_set_debug(readCtx_, TRUE);
-    }
+    modbus_set_slave(readCtx_, 1);
+    // modbus_set_debug(readCtx_, TRUE);
+}
 
-    MBConnection::~MBConnection()
-    {
-        //Close the connection before removing resources
-        //disable(); must be done before removing resource
-        modbus_free(writeCtx_);
-        modbus_free(readCtx_);
-    }
+MBConnection::~MBConnection()
+{
+    // Close the connection before removing resources
+    // disable(); must be done before removing resource
+    modbus_free(writeCtx_);
+    modbus_free(readCtx_);
+}
 
-    bool MBConnection::open(PLC* thePLC)
-    {
-        int readErr = modbus_connect(readCtx_);
-        int writeErr = modbus_connect(writeCtx_);
-        return ( (readErr != -1) && (writeErr != -1));
-    }
+bool MBConnection::open(PLC *thePLC)
+{
+    int readErr = modbus_connect(readCtx_);
+    int writeErr = modbus_connect(writeCtx_);
+    return ( (readErr != -1) && (writeErr != -1));
+}
 
-    bool MBConnection::close(PLC* thePLC)
-    {
-        modbus_close(readCtx_);
-        modbus_close(writeCtx_);
-        return true;
-    }
+bool MBConnection::close(PLC *thePLC)
+{
+    modbus_close(readCtx_);
+    modbus_close(writeCtx_);
+    return true;
+}
+
+int MBConnection::readFrames(modbus_t *ctx, long start_addr, uint16_t count, uint8_t *data, int IO)
+{
+    uint16_t *destp = (uint16_t *)data;
+    int word_count, byte_count;
 
-    /*----------------------------------------------------------*/
-    int MBConnection::mbWriteFrames(modbus_t* ctx, long start_addr, uint16_t count, uint8_t* data)
+    while (count)
     {
-        uint16_t *srcp = (uint16_t*)data;
-        uint16_t word_count, byte_count;
 
-        while (count)
+        if (count > MAX_READ_DATA_SIZE)
         {
-            word_count = static_cast<uint16_t>((count > MAX_WRITE_DATA_SIZE) ? MAX_WRITE_DATA_SIZE / 2 : (count / 2) + (count % 2));
-            byte_count = static_cast<uint16_t>(word_count * 2);
+            /*'word_count' is a word counter*/
+            word_count = MAX_READ_DATA_SIZE / 2;
+        }
+        else
+        {
+            /*'word_count' is a word counter*/
+            word_count = (count / 2) + (count % 2);
+        }
 
-            if (modbus_write_registers(ctx, static_cast<int>(start_addr), static_cast<int>(word_count), srcp) != word_count)
-            {
-                return -1;
-            }
+        byte_count = (word_count * 2);
 
-            //srcp += byte_count-(count%2);
-            srcp += word_count;
-            start_addr += word_count;
-            count = static_cast<uint16_t>(count - (byte_count - (count % 2)));
+        if (IO)
+        {
+            if (modbus_read_input_registers(ctx, static_cast<int>(start_addr), static_cast<int>(word_count), destp) != word_count)
+                return -1;
+        }
+        else
+        {
+            if (modbus_read_registers(ctx, static_cast<int>(start_addr), static_cast<int>(word_count), destp) != word_count)
+                return -1;
         }
 
-        return 0;
+        // destp += (byte_count-(count%2));
+        destp += word_count;
+        start_addr += word_count;
+        count = static_cast<uint16_t>(count - (byte_count - (count % 2)));
     }
 
-    /*----------------------------------------------------------*/
-    int MBConnection::mbReadFrames(modbus_t* ctx, long start_addr, uint16_t count, uint8_t* data)
+    return 0;
+}
+
+int MBConnection::writeFrames(modbus_t *ctx, long start_addr, uint16_t count, uint8_t *data)
+{
+    uint16_t *srcp = (uint16_t *)data;
+    uint16_t word_count, byte_count;
+
+    while (count)
     {
-        uint16_t *destp = (uint16_t*)data;
-        int word_count, byte_count;
+        word_count = static_cast<uint16_t>( (count > MAX_WRITE_DATA_SIZE) ? MAX_WRITE_DATA_SIZE / 2 : (count / 2) + (count % 2));
+        byte_count = static_cast<uint16_t>(word_count * 2);
 
-        while (count)
+        if (modbus_write_registers(ctx, static_cast<int>(start_addr), static_cast<int>(word_count), srcp) != word_count)
         {
+            return -1;
+        }
 
-            if (count > MAX_READ_DATA_SIZE)
-            {
-                /*'word_count' is a word counter*/
-                word_count = MAX_READ_DATA_SIZE / 2;
-            }
-            else
-            {
-                /*'word_count' is a word counter*/
-                word_count = (count / 2) + (count % 2);
-            }
+        // srcp += byte_count-(count%2);
+        srcp += word_count;
+        start_addr += word_count;
+        count = static_cast<uint16_t>(count - (byte_count - (count % 2)));
+    }
 
-            byte_count = (word_count * 2);
+    return 0;
+}
 
-            if (modbus_read_registers(ctx, static_cast<int>(start_addr), static_cast<int>(word_count), destp) != word_count)
-            {
-                return -1;
-            }
+int MBConnection::readCoils(modbus_t *ctx, long start_addr, uint16_t count, uint8_t *data)
+{
+    // Need one byte for each bit in buffer, e.g 2 bytes would require a 16 byte array
+    uint8_t c[count * 8];
+    uint16_t bit_count;
+    uint16_t current_count;
+    uint16_t current_bit_count;
+
+    int j = 0;
+    int i = 0;
+    current_count = static_cast<uint16_t>(count * 8);
+    // Byte address to bit address
+    start_addr *= 8;
+    j = 0;
+
+    while (current_count)
+    {
+        bit_count = static_cast<uint16_t>( (current_count > MODBUS_MAX_READ_BITS) ? MODBUS_MAX_READ_BITS : current_count);
 
-            //destp += (byte_count-(count%2));
-            destp += word_count;
-            start_addr += word_count;
-            count = static_cast<uint16_t>(count - (byte_count - (count % 2)));
+        if (modbus_read_input_bits(ctx, static_cast<int>(start_addr), bit_count, &c[j]) < 0)
+        {
+            return -1;
         }
 
-        return 0;
+        j += bit_count;
+        start_addr += bit_count;
+        current_count = static_cast<uint16_t>(current_count - bit_count);
     }
 
-    int MBConnection::readData(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+    // The buffer is filled with boolean (8 bits) data that need to be converted back to a bitstream
+
+    for (i = 0; i < count; i++)
     {
-        int err = 0;
+        current_bit_count = static_cast<uint16_t>(i * 8);
+        data[i] = 0x00;
+        for (j = 0; j < 8; j++)
+        {
+            if (c[current_bit_count + j])
+                data[i] |= static_cast<unsigned char>(1 << (7 - j));
+        }
+    }
 
-        //Schneider uses 16bit alignment memory. Block address is expressed in bytes, must be an even value!
-        if (address % 2)
-        throw SilecsException(__FILE__, __LINE__, PARAM_INCORRECT_BLOCK_ADDRESS, StringUtilities::toString(address));
+    return 0;
+}
 
-        /* . There is one read-channel per PLC connection. It must be protected against concurrent access.
-         * . The write-channel is independent and can be accessed in parallel.
-         * . The global action (open,close,etc.) must be protected from any concurrent access.
-         * Attention!
-         * Mutexes are defined with recursive option. It allows doOpen method re-calling readData
-         * method by executing register synchronization if required.
-         */
+int MBConnection::writeCoils(modbus_t *ctx, long start_addr, uint16_t count, uint8_t *data)
+{
+    string tmp;
 
-        //(re)connect the PLC if needed and (re)synchronize the retentive registers
-        if (doOpen(thePLC))
-        {
-            //connection is established then acquire data
-            readLock();
-            //Schneider uses 16bit alignment memory. Block address is expressed in bytes (==> /2)
-            long addr = (address + offset) / 2;
+    // Need one byte for each bit in data, e.g 2 bytes would require a 16 byte array
+    uint8_t c[count * 8];
+    uint16_t bit_count;
 
-            //DATA topic makes sense with RECV one
-            if (RECV & Log::topics_)
-            LOG(DATA) << "Read data, address: %MW" << addr << ", byte-size: " << size;
+    int i, k;
+    int j = 0;
 
-            err = mbReadFrames(readCtx_, addr, (unsigned short)size, pBuffer);
-            checkError(thePLC, err, false);// close the connection, will try again at the next access
-            readUnlock();
+    // Convert the bitstream to a bool array
+    for (i = 0; i < count; i++)
+    {
+        std::bitset<8> b(data[i]);
+        tmp = b.to_string();
+        std::copy(tmp.begin(), tmp.end(), &c[j]);
+        for (k = j; k < j + 8; k++)
+        {
+            // Convert char to int
+            c[k] = static_cast<uint8_t>(c[k] - '0');
         }
-        return err;
+        j += 8;
     }
 
-    int MBConnection::writeData(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
-    {
-        int err = 0;
+    count = static_cast<uint16_t>(count * 8);
+    // Byte address to bit address
+    start_addr *= 8;
+    j = 0;
 
-        //Schneider uses 16bit alignment memory. Block address is expressed in bytes, must be an even value!
-        if (address % 2)
-        throw SilecsException(__FILE__, __LINE__, PARAM_INCORRECT_BLOCK_ADDRESS, StringUtilities::toString(address));
-
-        /* . There is one read-channel per PLC connection. It must be protected against concurrent access.
-         * . The write-channel is independent and can be accessed in parallel.
-         * . The global action (open,close,etc.) must be protected from any concurrent access.
-         * Attention!
-         * Mutexes are defined with recursive option. It allows doOpen method re-calling sendData
-         * method by executing register synchronization if required.
-         */
+    while (count)
+    {
+        bit_count = static_cast<uint16_t>( (count > MODBUS_MAX_WRITE_BITS) ? MODBUS_MAX_WRITE_BITS : count);
 
-        //(re)connect the PLC if needed and (re)synchronize the retentive registers
-        if (doOpen(thePLC))
+        if (modbus_write_bits(ctx, static_cast<int>(start_addr), bit_count, &c[j]) < 0)
         {
-            //connection is established then send data
-            writeLock();
-            //Schneider uses 16bit alignment memory. Block address is expressed in bytes (==> /2)
-            long addr = (address + offset) / 2;
-
-            //DATA topic makes sense with SEND one
-            if (SEND & Log::topics_)
-            LOG(DATA) << "Write data, address: %MW" << addr << ", byte-size: " << size;
-
-            err = mbWriteFrames(writeCtx_, (unsigned short)addr, (unsigned short)size, pBuffer);
-            checkError(thePLC, err, false);// close the connection, will try again at the next access
-            writeUnlock();
+            return -1;
         }
-        return err;
+
+        j += bit_count;
+        start_addr += bit_count;
+        count = static_cast<uint16_t>(count - bit_count);
     }
+    return 0;
+}
+
+int MBConnection::readRegisters(PLC *thePLC, long address, unsigned long offset, unsigned long size, unsigned char *pBuffer, int IO)
+{
+    int err = 0;
 
-    int MBConnection::readMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+    // Schneider uses 16bit alignment memory. Block address is expressed in
+    // bytes, must be an even value!
+    if (address % 2)
+        throw SilecsException(__FILE__, __LINE__, PARAM_INCORRECT_BLOCK_ADDRESS, StringUtilities::toString(address));
+
+    /* . There is one read-channel per PLC connection. It must be protected
+     * against concurrent access.
+     * . The write-channel is independent and can be accessed in parallel.
+     * . The global action (open,close,etc.) must be protected from any
+     * concurrent access.
+     * Attention!
+     * Mutexes are defined with recursive option. It allows doOpen method
+     * re-calling readData
+     * method by executing register synchronization if required.
+     */
+
+    //(re)connect the PLC if needed and (re)synchronize the retentive registers
+    if (doOpen(thePLC))
     {
-        return readData(thePLC, address, offset, size, pBuffer);
+        // connection is established then acquire data
+        readLock();
+        // Schneider uses 16bit alignment memory. Block address is expressed in
+        // bytes (==> /2)
+        long addr = (address + offset) / 2;
+
+        // DATA topic makes sense with RECV one
+        if (RECV & Log::topics_)
+            LOG(DATA) << "Read data, address: %MW" << addr << ", byte-size: " << size;
+
+        err = readFrames(readCtx_, addr, (unsigned short)size, pBuffer, IO);
+        checkError(thePLC, err, false); // close the connection, will try again
+                                        // at the next access
+        readUnlock();
     }
+    return err;
+}
+
+int MBConnection::writeRegisters(PLC *thePLC, long address, unsigned long offset, unsigned long size, unsigned char *pBuffer)
+{
+    int err = 0;
+
+    // Schneider uses 16bit alignment memory. Block address is expressed in
+    // bytes, must be an even value!
+    if (address % 2)
+        throw SilecsException(__FILE__, __LINE__, PARAM_INCORRECT_BLOCK_ADDRESS, StringUtilities::toString(address));
 
-    int MBConnection::writeMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+    /* . There is one read-channel per PLC connection. It must be protected
+     * against concurrent access.
+     * . The write-channel is independent and can be accessed in parallel.
+     * . The global action (open,close,etc.) must be protected from any
+     * concurrent access.
+     * Attention!
+     * Mutexes are defined with recursive option. It allows doOpen method
+     * re-calling sendData
+     * method by executing register synchronization if required.
+     */
+
+    //(re)connect the PLC if needed and (re)synchronize the retentive registers
+    if (doOpen(thePLC))
     {
-        return writeData(thePLC, address, offset, size, pBuffer);
+        // connection is established then send data
+        writeLock();
+        // Schneider uses 16bit alignment memory. Block address is expressed in
+        // bytes (==> /2)
+        long addr = (address + offset) / 2;
+
+        // DATA topic makes sense with SEND one
+        if (SEND & Log::topics_)
+            LOG(DATA) << "Write data, address: %MW" << addr << ", byte-size: " << size;
+
+        err = writeFrames(writeCtx_, (unsigned short)addr, (unsigned short)size, pBuffer);
+        checkError(thePLC, err, false); // close the connection, will try again
+                                        // at the next access
+        writeUnlock();
     }
+    return err;
+}
 
-    int MBConnection::readIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+int MBConnection::readBits(PLC *thePLC, long address, unsigned long offset, unsigned long size, unsigned char *pBuffer)
+{
+    int err = 0;
+
+    /* . There is one read-channel per PLC connection. It must be protected
+     * against concurrent access.
+     * . The write-channel is independent and can be accessed in parallel.
+     * . The global action (open,close,etc.) must be protected from any
+     * concurrent access.
+     * Attention!
+     * Mutexes are defined with recursive option. It allows doOpen method
+     * re-calling readData
+     * method by executing register synchronization if required.
+     */
+
+    //(re)connect the PLC if needed and (re)synchronize the retentive registers
+    if (doOpen(thePLC))
     {
-        return readData(thePLC, address, offset, size, pBuffer);
+        // connection is established then acquire data
+        readLock();
+        long addr = address + offset;
+
+        // DATA topic makes sense with RECV one
+        if (RECV & Log::topics_)
+            LOG(DATA) << "Read bits, address: %MW" << addr << ", byte-size: " << size;
+
+        err = readCoils(readCtx_, addr, (unsigned short)size, pBuffer);
+        checkError(thePLC, err, false); // close the connection, will try again
+                                        // at the next access
+        readUnlock();
     }
+    return err;
+}
 
-    int MBConnection::writeIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+int MBConnection::writeBits(PLC *thePLC, long address, unsigned long offset, unsigned long size, unsigned char *pBuffer)
+{
+    int err = 0;
+
+    /* . There is one read-channel per PLC connection. It must be protected
+     * against concurrent access.
+     * . The write-channel is independent and can be accessed in parallel.
+     * . The global action (open,close,etc.) must be protected from any
+     * concurrent access.
+     * Attention!
+     * Mutexes are defined with recursive option. It allows doOpen method
+     * re-calling sendData
+     * method by executing register synchronization if required.
+     */
+
+    //(re)connect the PLC if needed and (re)synchronize the retentive registers
+    if (doOpen(thePLC))
     {
-        return writeData(thePLC, address, offset, size, pBuffer);
+        // connection is established then send data
+        writeLock();
+
+        long addr = address + offset;
+
+        // DATA topic makes sense with SEND one
+        if (SEND & Log::topics_)
+            LOG(DATA) << "Write data, address: %MW" << addr << ", byte-size: " << size;
+
+        err = writeCoils(writeCtx_, (unsigned short)addr, (unsigned short)size, pBuffer);
+        checkError(thePLC, err, false); // close the connection, will try again
+                                        // at the next access
+        writeUnlock();
     }
+    return err;
+}
+
+int MBConnection::readMemory(PLC *thePLC, long address, unsigned long offset, unsigned long size, unsigned char *pBuffer)
+{
+    return readRegisters(thePLC, address, offset, size, pBuffer, 0);
+}
+
+int MBConnection::writeMemory(PLC *thePLC, long address, unsigned long offset, unsigned long size, unsigned char *pBuffer)
+{
+    return writeRegisters(thePLC, address, offset, size, pBuffer);
+}
+
+int MBConnection::readAIO(PLC *thePLC, long address, unsigned long offset, unsigned long size, unsigned char *pBuffer)
+{
+    return readRegisters(thePLC, address, offset, size, pBuffer, 1);
+}
+
+int MBConnection::writeAIO(PLC *thePLC, long address, unsigned long offset, unsigned long size, unsigned char *pBuffer)
+{
+    return writeRegisters(thePLC, address, offset, size, pBuffer);
+}
+
+int MBConnection::readDIO(PLC *thePLC, long address, unsigned long offset, unsigned long size, unsigned char *pBuffer)
+{
+    return readBits(thePLC, address, offset, size, pBuffer);
+}
+
+int MBConnection::writeDIO(PLC *thePLC, long address, unsigned long offset, unsigned long size, unsigned char *pBuffer)
+{
+    return writeBits(thePLC, address, offset, size, pBuffer);
+}
 
 //-------------------------------------------------------------------------------------------------------------------
-    bool MBConnection::checkError(PLC* thePLC, int err, bool retry)
+bool MBConnection::checkError(PLC *thePLC, int err, bool retry)
+{
+
+    if (err != 0)
     {
+        LOG(COMM) << "Transaction failure with the controller: " << thePLC->getName() << ". MODBUS[" << errno << "]: " << modbus_strerror(errno);
 
-        if (err != 0)
+        if (retry)
         {
-            LOG(COMM) << "Transaction failure with the controller: " << thePLC->getName() << ". MODBUS[" << errno << "]: " << modbus_strerror(errno);
-
-            if (retry)
-            {
-                LOG(COMM) << "Try to reconnect the controller: " << thePLC->getName();
-                if (reOpen(thePLC))
-                { // can repeat the request after the connection was successfully reopened
-                    return true;
-                }
-                // reconnection has failed again, just close the connection
-                LOG(COMM) << "Unable to reconnect the controller: " << thePLC->getName();
+            LOG(COMM) << "Try to reconnect the controller: " << thePLC->getName();
+            if (reOpen(thePLC))
+            { // can repeat the request after the connection was successfully
+              // reopened
+                return true;
             }
-            // no retry, we just want to close (use default case)
-            doClose(thePLC, /*withLock =*/true);
+            // reconnection has failed again, just close the connection
+            LOG(COMM) << "Unable to reconnect the controller: " << thePLC->getName();
         }
-        return false;
+        // no retry, we just want to close (use default case)
+        doClose(thePLC, /*withLock =*/true);
     }
+    return false;
+}
 
 } // namespace
 
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.h b/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.h
index 98ef27e..4b9ed19 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.h
@@ -21,36 +21,49 @@
 
 namespace Silecs
 {
-    class PLC;
-    class Connection;
-
-    /*!
-     * \class MBConnection
-     * \brief Plc-communication object for specific Modbus protocol
-     */
-    class MBConnection : public Connection
-    {
-
-    public:
-        MBConnection(PLC* thePLC);
-        virtual ~MBConnection();
-
-        int readMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-        int writeMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-        int readIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-        int writeIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-
-    private:
-        modbus_t* readCtx_;
-        modbus_t* writeCtx_;
-        bool open(PLC* thePLC);
-        bool close(PLC* thePLC);
-        int readData(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-        int writeData(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-        int mbWriteFrames(modbus_t* ctx, uint16_t dataAddr, uint16_t dataSize, uint8_t* dataBuffer);
-        int mbReadFrames(modbus_t* ctx, uint16_t dataAddr, uint16_t dataSize, uint8_t* dataBuffer);
-        bool checkError(PLC* thePLC, int err, bool retry);
-    };
+class PLC;
+class Connection;
+
+/*!
+ * \class MBConnection
+ * \brief Plc-communication object for specific Modbus protocol
+ */
+class MBConnection : public Connection
+{
+
+public:
+    MBConnection(PLC* thePLC);
+    virtual ~MBConnection();
+
+    int readMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int writeMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int readAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int writeAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int readDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int writeDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+
+private:
+    modbus_t* readCtx_;
+    modbus_t* writeCtx_;
+    bool open(PLC* thePLC);
+    bool close(PLC* thePLC);
+
+    // This set of functions are used to address the analog and memory registers
+    int readRegisters(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer, int IO);
+    int writeRegisters(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    // read and write Frames interfere directly with the libmodbus API
+    int readFrames(modbus_t* ctx, long dataAddr, uint16_t dataSize, uint8_t* dataBuffer, int IO);
+    int writeFrames(modbus_t* ctx, long dataAddr, uint16_t dataSize, uint8_t* dataBuffer);
+
+    // This set of functions are used to address the digital registers
+    int readBits(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int writeBits(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    // read and write Coils interfere directly with the libmodbus API
+    int readCoils(modbus_t* ctx, long dataAddr, uint16_t dataSize, uint8_t* dataBuffer);
+    int writeCoils(modbus_t* ctx, long dataAddr, uint16_t dataSize, uint8_t* dataBuffer);
+
+    bool checkError(PLC* thePLC, int err, bool retry);
+};
 
 } // namespace
 
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.cpp b/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.cpp
index a28c4ce..0c11c1b 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.cpp
@@ -298,6 +298,23 @@ int SNAP7Connection::writeIO(PLC* thePLC, long address, unsigned long offset, un
     return err;
 }
 
+int SNAP7Connection::readAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+{
+    return readIO(thePLC, address, offset, size, pBuffer);
+}
+int SNAP7Connection::writeAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+{
+    return writeIO(thePLC, address, offset, size, pBuffer);
+}
+int SNAP7Connection::readDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+{
+    return readIO(thePLC, address, offset, size, pBuffer);
+}
+int SNAP7Connection::writeDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer)
+{
+    return writeIO(thePLC, address, offset, size, pBuffer);
+}
+
 //-------------------------------------------------------------------------------------------------------------------
 bool SNAP7Connection::checkError(PLC* thePLC, int err, bool retry)
 {
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.h b/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.h
index 4fc1ace..7e53000 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.h
@@ -45,8 +45,10 @@ public:
     int writeData(PLC* thePLC, long DBn, unsigned long offset, unsigned long size, unsigned char* pBuffer);
     int readMemory(PLC* thePLC, long DBn, unsigned long offset, unsigned long size, unsigned char* pBuffer);
     int writeMemory(PLC* thePLC, long DBn, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-    int readIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
-    int writeIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int readAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int writeAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int readDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int writeDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
 
     //Extension Silecs methods
     int coldRestart(PLC* thePLC);
@@ -57,6 +59,8 @@ private:
     S7Object sendClient_;
     bool open(PLC* thePLC);
     bool close(PLC* thePLC);
+    int readIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
+    int writeIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* pBuffer);
 
     int rackNb_; //rackNb - common rack is 0 by default
     int slotNb_; //slotNb - depends on hardware configuration (scan is required the first connect)
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.h b/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.h
index 9e4e682..7c668b0 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.h
@@ -49,9 +49,14 @@ public:
 
     virtual int writeMemory(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* buffer) = 0;
 
-    virtual int readIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* buffer) = 0;
+    virtual int readAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* buffer) = 0;
+
+    virtual int writeAIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* buffer) = 0;
+
+    virtual int readDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* buffer) = 0;
+
+    virtual int writeDIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* buffer) = 0;
 
-    virtual int writeIO(PLC* thePLC, long address, unsigned long offset, unsigned long size, unsigned char* buffer) = 0;
     /*!
      * \fn enable/disable
      * \brief The client can suspend the data transmission by disabling the connection if required.
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.cpp b/silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.cpp
index 92230bc..aa9a7b1 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/core/PLCRecvAction.cpp
@@ -96,9 +96,15 @@ int PLCRecvBlockMode::execute(Context* pContext)
             switch (theBlock_->getAccessArea())
             {
                 case 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 Analog:
                 {
-                    errorCode = pConn->readIO(theBlock_->getPLC(), usedAddress, //Base address (or DBn) of the block
+                    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
@@ -151,9 +157,15 @@ int PLCRecvBlockMode::execute(Context* pContext)
             switch (area)
             {
                 case 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 Analog:
                 {
-                    errorCode = pConn->readIO(theBlock_->getPLC(), theBlock_->getAddress(), //Base address (or DBn) of the block
+                    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)
@@ -239,9 +251,15 @@ int PLCRecvDeviceMode::execute(Context* pContext)
             switch (theBlock_->getAccessArea())
             {
                 case 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 Analog:
                 {
-                    errorCode = pConn->readIO(theBlock_->getPLC(), usedDeviceAddress, //Base address (or DBn) of the device
+                    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
@@ -299,14 +317,20 @@ int PLCRecvDeviceMode::execute(Context* pContext)
                 switch (area)
                 {
                     case 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 Analog:
                     {
-                        errorCode = pConn->readIO(theBlock_->getPLC(), pDev->getInputAddress(area), //Base address (or DBn) of the device
+                        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 Memory:
                     default:
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.cpp b/silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.cpp
index b34efdd..1783ab8 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/core/PLCSendAction.cpp
@@ -95,9 +95,15 @@ int PLCSendBlockMode::execute(Context* pContext)
                 switch (area)
                 {
                     case 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 Analog:
                     {
-                        errorCode = pConn->writeIO(theBlock_->getPLC(), usedAddress, //Base address (or DBn) of the block
+                        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
@@ -153,9 +159,15 @@ int PLCSendBlockMode::execute(Context* pContext)
                 switch (theBlock_->getAccessArea())
                 {
                     case 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 Analog:
                     {
-                        errorCode = pConn->writeIO(theBlock_->getPLC(), theBlock_->getAddress(), //Base address (or DBn) of the block
+                        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)
@@ -230,9 +242,15 @@ int PLCSendDeviceMode::execute(Context* pContext)
                 switch (area)
                 {
                     case 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 Analog:
                     {
-                        errorCode = pConn->writeIO(theBlock_->getPLC(), usedDeviceAddress, //Base address (or DBn) of the device
+                        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
@@ -285,9 +303,15 @@ int PLCSendDeviceMode::execute(Context* pContext)
                     switch (area)
                     {
                         case 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 Analog:
                         {
-                            errorCode = pConn->writeIO(theBlock_->getPLC(), pDev->getOutputAddress(area), //Base address (or DBn) of the device
+                            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
-- 
GitLab