From 3fbf61e0dfd7cae5a91ef0ed408418ba168a2381 Mon Sep 17 00:00:00 2001
From: "m.nabywaniec" <mateusz.nabywaniec@s2innovation.com>
Date: Fri, 18 Nov 2022 09:10:37 +0000
Subject: [PATCH] Add support for boolean scalar data-type (Issue #9)

- support only for scalar booleans for the first. There will be an exception when trying to create boolean arrays
- Simplified setting values for bool registers - using `SetAtBit` and `GetBitAt` from snap7.
- In diagnostic tool display values for booleans as true or false but 0 and 1 also parsed to boolean from user

MR !20
---
 silecs-codegen/src/xml/genparam.py            | 52 ++++++++++++++--
 .../src/xml/model/Class/Register.py           | 24 ++++++--
 silecs-codegen/src/xml/s7template.py          | 61 ++++++++++---------
 .../interface/communication/SNAP7Utils.cpp    | 31 ++++++++++
 .../interface/communication/SNAP7Utils.h      | 14 +++++
 .../interface/equipment/PLCRegister.cpp       | 20 ++++++
 .../interface/equipment/SilecsRegister.cpp    | 52 ++++++++++++++++
 .../interface/equipment/SilecsRegister.h      | 22 ++++++-
 .../interface/utility/StringUtilities.cpp     | 12 ++++
 .../interface/utility/StringUtilities.h       | 29 ++++++++-
 .../src/silecs-diagnostic/utils.cpp           | 26 ++++++++
 silecs-model/src/xml/DesignSchema.xsd         |  1 +
 12 files changed, 303 insertions(+), 41 deletions(-)
 create mode 100644 silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Utils.cpp
 create mode 100644 silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Utils.h

diff --git a/silecs-codegen/src/xml/genparam.py b/silecs-codegen/src/xml/genparam.py
index 12c8c4b..37ea5ca 100644
--- a/silecs-codegen/src/xml/genparam.py
+++ b/silecs-codegen/src/xml/genparam.py
@@ -36,6 +36,9 @@ from model.Deploy.Controller import *
 import model.Deploy.Device
 from model.Param.Param import *
 
+# maximum number of boolean bits in the same register address
+MAX_BITS_IN_ADDRESS = 8
+
 #-------------------------------------------------------------------------
 # Trimming: remove new-line, tabulation and spaces of the string
 # Used to compute CRC32 on significant data only
@@ -151,9 +154,37 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
                 blockSize = 0           # block size (sum of the register size)
                 blockMemSize = 0    # block size (sum of the regist)
                 controller.regCounter = 0      # used for NI register address generation
+
+                # additional data for managing booleans
+                isBooleanRegister = False
+                bitNumber = 0
+                nextRegister = None
+
                 # INNER LOOP TO ACCESS AT REGISTER LEVEL
-                for designRegister in designBlock.getDesignRegisters():
+                designRegisters = designBlock.getDesignRegisters()
+                for i, designRegister in enumerate(designRegisters):
+                    if i + 1 != len(designRegisters):
+                        nextRegister = designRegisters[i+1]
+                    else: 
+                        nextRegister = None
+
                     iecommon.logDebug("------ Processing Register " + designRegister.name + " ------",logTopics)
+
+                    # Allow bool register only for Siemens controller
+                    if designRegister.format == 'bool':
+                        isBooleanRegister = True
+
+                        if not designRegister.isScalar():
+                            iecommon.logError("Invalid format in register: %s Bool format available only for scalars" %designRegister.name, False, logTopics)
+                            sys.exit(2)
+
+                        if controller.brand != 'SIEMENS':
+                            iecommon.logError("Invalid format in register: %s. Bool format available only on SIEMENS controllers" %designRegister.name, False,logTopics)
+                            sys.exit(2)
+                    else:
+                        isBooleanRegister = False
+                        bitNumber = 0
+
                     # Set length attribute only for string registers
                     if designRegister.format == 'string': # TODO: Port this constraint to java
                         if controller.brand == 'DIGI':
@@ -164,12 +195,25 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
                                 sys.exit(2)
                     
                     regAddress = controller.alignRegAddress(designBlock, designRegister, regAddress)
+                    
                     controller.regCounter += 1 # used for NI register address generation
                     # Set register mem-size
                     paramRegister = ParamRegister()
-                    paramRegister.initWithDesignRegister(designRegister,regAddress,controller.msize)
-                    # Compute address for the next register
-                    regAddress = controller.computeNextRegAddress(designBlock, designRegister, regAddress)
+
+                    if isBooleanRegister:
+                        paramRegister.initWithDesignRegister(designRegister,regAddress,controller.msize, bitNumber)
+                    else:
+                        paramRegister.initWithDesignRegister(designRegister,regAddress,controller.msize)
+
+                    if isBooleanRegister and nextRegister and nextRegister.format == "bool":
+                        bitNumber = (bitNumber + 1) % MAX_BITS_IN_ADDRESS
+                        if bitNumber == 0:
+                            # Compute address for the next register if max bits in address achieved
+                            regAddress = controller.computeNextRegAddress(designBlock, designRegister, regAddress)
+                    else:
+                        # Compute address for the next register
+                        regAddress = controller.computeNextRegAddress(designBlock, designRegister, regAddress)
+                    
                     paramBlock.xmlNode.addChild(paramRegister.xmlNode)
                     #paramRegister.xmlNode.shellPrintNode()
                     #iterativelly compute the block size (accumulator initialized outside the loop)
diff --git a/silecs-codegen/src/xml/model/Class/Register.py b/silecs-codegen/src/xml/model/Class/Register.py
index 35d6d95..ab52f73 100644
--- a/silecs-codegen/src/xml/model/Class/Register.py
+++ b/silecs-codegen/src/xml/model/Class/Register.py
@@ -106,6 +106,7 @@ class Register(object):
     
     def getCType(self):
         return  {
+                 'bool'     :'bool',
                  'int8'     :'int8_t',
                  'uint8'    :'uint8_t',
                  'int16'    :'int16_t',
@@ -160,7 +161,8 @@ class Register(object):
                  'float32'      :'float32',
                  'float64'      :'float64',
                  'date'         :'date',
-                 'string'       : 'string'
+                 'string'       : 'string',
+                 'bool'         : 'bool'
         }[self.format]
 
     #Needed for SilecsMethodNames
@@ -177,6 +179,7 @@ class Register(object):
 
     def getDataSize(self):
         return {
+            'bool'        : 1,
             'uint8'       : 1,
             'int8'        : 1,
             'uint16'      : 2,
@@ -208,6 +211,7 @@ class ParamRegister(Register):
         self.size = 0
         self.address = 0
         self.memSize = 0
+        self.bitNumber = 0
         
     def initWithParamRegisterNode(self, xmlNode):
         super(ParamRegister, self).__init__(xmlNode)
@@ -215,7 +219,12 @@ class ParamRegister(Register):
         self.address = int(self.xmlNode.prop("address"))
         self.memSize = int(self.xmlNode.prop("mem-size"))
         
-    def initWithDesignRegister(self, designRegister,address,memSize):
+        if self.xmlNode.prop("bit-number"):
+            self.bitNumber = int(self.xmlNode.prop("bit-number"))
+        
+
+
+    def initWithDesignRegister(self, designRegister,address,memSize,bitNumber=None):
         newNodeTree = designRegister.xmlNode.copyNode(1) # 1 is for recursive copy
         if ( newNodeTree == None ):
             iecommon.logError('ERROR: Failed to copy register node: '+ designRegister.name +'.', True, {'errorlog': True})
@@ -227,6 +236,11 @@ class ParamRegister(Register):
         self.address = address
         newNodeTree.newProp("mem-size", str(memSize))
         self.memSize = memSize
+
+        if bitNumber is not None:
+            newNodeTree.newProp("bit-number", str(bitNumber))
+            self.bitNumber = bitNumber
+
         #self.xmlNode.shellPrintNode()
         
     def setSize(self,size):
@@ -283,7 +297,8 @@ class DesignRegister(Register):
                  'dint'     :'int32_t',
                  'real'     :'float',
                  'dt'       :'double',
-                 'string'   :'char'
+                 'string'   :'char',
+                 'bool'     :'bool'
         }[self.getSilecsDataType()]
 
     def getFesaFieldNameCapitalized(self):
@@ -310,5 +325,6 @@ class DesignRegister(Register):
                  'dint'     :'int32_t',
                  'real'     :'float',
                  'dt'       :'double',
-                 'string'   :'char'
+                 'string'   :'char',
+                 'bool'     :'bool'
         }[silecsDataType]
diff --git a/silecs-codegen/src/xml/s7template.py b/silecs-codegen/src/xml/s7template.py
index 798081c..285ef9b 100644
--- a/silecs-codegen/src/xml/s7template.py
+++ b/silecs-codegen/src/xml/s7template.py
@@ -25,10 +25,11 @@
 # Global definition
 #=========================================================================
 
-#-------------------------------------------------------------------------
+#-------------------------------------------------------------------------
 # Hash-Table
 
 whichSimaticFormat = {
+    'bool'    : 'BOOL',
 	'uint8'   : 'BYTE',
 	'int8'    : 'CHAR',
 	'uint16'  : 'WORD',
@@ -41,19 +42,20 @@ whichSimaticFormat = {
 #	'int64'   : '8',	not supported with PLC
 #	'float64' : '8',    not supported with PLC
 	'date'    : 'DT',
-	'char'	:'CHAR',
-    'byte'	:'BYTE',
-    'word'	:'WORD',
-    'dword'	:'DWORD',
-    'int'	:'INT',
-    'dint'	:'DINT',
-    'real'	:'REAL',
-    'dt'	:'DT'
}
+	'char'	:'CHAR',
+    'byte'	:'BYTE',
+    'word'	:'WORD',
+    'dword'	:'DWORD',
+    'int'	:'INT',
+    'dint'	:'DINT',
+    'real'	:'REAL',
+    'dt'	:'DT'
+}
 
 
 #=========================================================================
 # STL Source template (.scl file)
-#=========================================================================
+#=========================================================================
 firstBlockUDT = """\r
 (* ---------------------------------------------------------------------\r
  * %s/ v%s\r
@@ -61,20 +63,20 @@ firstBlockUDT = """\r
  * ---------------------------------------------------------------------\r
  *)\r
 """
-
-blockUDT = """TYPE _%s_%s\r
-AUTHOR:    %s\r
-FAMILY:    SILECS\r
-NAME:      UDTB\r
-    STRUCT\r
-%s\r
-    END_STRUCT;\r
+
+blockUDT = """TYPE _%s_%s\r
+AUTHOR:    %s\r
+FAMILY:    SILECS\r
+NAME:      UDTB\r
+    STRUCT\r
+%s\r
+    END_STRUCT;\r
 END_TYPE\r
-\r
+\r
 """
 
 regScalar = """        %s: %s%s;\r
-"""
+"""
 
 regArray  = """        %s: ARRAY[0..%d] OF %s%s;\r
 """
@@ -83,7 +85,7 @@ regArray2d =  """        %s: ARRAY[0..%d, 0..%d] OF %s%s;\r
 """
 
 regFixedStringLen = """		%s: STRING[16]%s;\r
-"""
+"""
 
 firstBlock = """\r
 (* ---------------------------------------------------------------------\r
@@ -94,15 +96,16 @@ firstBlock = """\r
 """
 
 DB_TIAP = """DATA_BLOCK %s_%s\r
-{ S7_Optimized_Access := 'FALSE' }\r
-AUTHOR:    %s\r
-FAMILY:    SILECS\r
-NAME:      %s\r
-STRUCT\r
%s\r
-END_STRUCT;\r
-BEGIN\r
+{ S7_Optimized_Access := 'FALSE' }\r
+AUTHOR:    %s\r
+FAMILY:    SILECS\r
+NAME:      %s\r
+STRUCT\r
+%s\r
+END_STRUCT;\r
+BEGIN\r
 END_DATA_BLOCK\r
-\r
+\r
 """
 
 DB_STEP7 = """(* %s_%s ...........................................*)\r
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Utils.cpp b/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Utils.cpp
new file mode 100644
index 0000000..d22bda8
--- /dev/null
+++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Utils.cpp
@@ -0,0 +1,31 @@
+#include "snap7.h"
+
+//******************************************************************************
+//           HELPER DATA ACCESS FUNCTIONS IMPLEMENTATION FOR SNAP 7
+//  They were no accessible in SNAP7 version 1.4.x
+//******************************************************************************
+
+//==============================================================================
+// Helper GET routines
+//==============================================================================
+
+bool GetBitAt(void *Buffer, int Pos, int Bit)
+{
+	byte Mask[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
+	if (Bit < 0) Bit = 0;
+    if (Bit > 7) Bit = 7;
+    return (*(pbyte(Buffer)+Pos) & Mask[Bit]) != 0;
+}
+
+//==============================================================================
+// Helper SET routines
+//==============================================================================
+void SetBitAt(void *Buffer, int Pos, int Bit, bool Value)
+{
+	byte Mask[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
+	pbyte p = pbyte(Buffer)+Pos;
+	if (Bit < 0) Bit = 0;
+    if (Bit > 7) Bit = 7;
+	(Value) ? *p |= Mask[Bit] : *p &= ~Mask[Bit];
+}
+
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Utils.h b/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Utils.h
new file mode 100644
index 0000000..e10f10f
--- /dev/null
+++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Utils.h
@@ -0,0 +1,14 @@
+#include <string>
+
+
+//******************************************************************************
+//                 HELPER DATA ACCESS FUNCTIONS FOR SNAP 7
+//  They were no accessible in SNAP7 version 1.4.x
+//******************************************************************************
+// GET 
+bool GetBitAt(void *Buffer, int Pos, int Bit);
+// SET
+void SetBitAt(void *Buffer, int Pos, int Bit, bool Value);
+
+
+
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCRegister.cpp b/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCRegister.cpp
index 12ed5eb..eef367f 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCRegister.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCRegister.cpp
@@ -14,6 +14,8 @@ Contributors:
 #include "PLCRegister.h"
 #include <silecs-communication/interface/communication/MBHardware.h>
 #include <silecs-communication/interface/communication/SNAP7Hardware.h>
+#include <silecs-communication/interface/communication/SNAP7Utils.h>
+#include <snap7.h>
 
 namespace Silecs
 {
@@ -303,6 +305,16 @@ void PLCRegister::importValue(void* pBuffer, timeval ts)
             }
         }
     }
+
+    else if ( format_ == Bool)
+    {
+        bool value = GetBitAt((void *)pBuffer, address_, bitNumber_);
+        if (RECV & Log::topics_)
+            LOG(RECV) << "Get bit: " << bitNumber_ << " of address " << address_ << ", value: " << value;
+        * ((bool*)pRecvValue_) = value;
+    }
+
+
     else
     {
         if (format_ != Date)
@@ -387,6 +399,14 @@ void PLCRegister::exportValue(void* pBuffer)
             }
         }
     }
+    else if ( format_ == Bool)
+    {
+        bool value = *((bool*) pSendValue_);
+        if (SEND & Log::topics_)
+            LOG(SEND) << "Set bit: " << bitNumber_ << " of address " << address_ << ", value: "<< value;
+        SetBitAt((void *)pBuffer, address_, bitNumber_, value);
+    }
+
     else
     {
         if (format_ != Date)
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.cpp b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.cpp
index 8685283..375a1c8 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.cpp
@@ -52,6 +52,9 @@ Register::Register(Device* theDevice, ElementXML* registerNode) :
     StringUtilities::fromString(size_, registerNode->getAttribute("size"));
     StringUtilities::fromString(memSize_, registerNode->getAttribute("mem-size"));
     StringUtilities::fromString(address_, registerNode->getAttribute("address"));
+    if ( registerNode->hasAttribute("bit-number"))
+        StringUtilities::fromString(bitNumber_, registerNode->getAttribute("bit-number"));
+
 
     if(registerNode->name_ == "Configuration-Register")
         isConfiguration_ = true;
@@ -111,6 +114,7 @@ PLC* Register::getPLC()
 {
     return theDevice_->getPLC();
 }
+
 Device* Register::getDevice()
 {
     return theDevice_;
@@ -443,6 +447,18 @@ double* Register::getRefDateArray2D(uint32_t& dim1, uint32_t& dim2)
 
 // .........................................................................
 //Recommended: from SLC6 (32bits and 64bits platform)
+bool Register::getValBool()
+{
+    if (!isReadable())
+        throw SilecsException(__FILE__, __LINE__, DATA_READ_ACCESS_TYPE_MISMATCH, getName());
+    if ( (format_ != Bool) || !isScalar())
+        throw SilecsException(__FILE__, __LINE__, PARAM_FORMAT_TYPE_MISMATCH, getName());
+
+    if (* ((int8_t*)pRecvValue_) == 0)
+        return false;
+    else
+        return true;
+}
 int8_t Register::getValInt8()
 {
     return getVal<int8_t>(Int8);
@@ -968,6 +984,19 @@ void Register::setValDateArray2D(const double* pVal, uint32_t dim1, uint32_t dim
 
 // .........................................................................
 //Recommended: from SLC6 (32bits and 64bits platform)
+void Register::setValBool(int32_t val)
+{
+    if (!isWritable())
+        throw SilecsException(__FILE__, __LINE__, DATA_WRITE_ACCESS_TYPE_MISMATCH, getName());
+    if ( (format_ != Bool) || (dimension1_ != 1))
+        throw SilecsException(__FILE__, __LINE__, PARAM_FORMAT_TYPE_MISMATCH, getName());
+
+    if ( val == 0 )
+        * ((bool*)pSendValue_) = false;
+    else
+        * ((bool*)pSendValue_) = true;
+    isInitialized_ = true;
+}
 void Register::setValInt8(int8_t val)
 {
     setVal<int8_t>(Int8, val);
@@ -1285,6 +1314,8 @@ FormatType Register::whichFormatType(std::string type)
         return Float64;
     else if (type == "string")
         return String;
+    else if (type == "bool")
+        return Bool;
     else
         throw SilecsException(__FILE__, __LINE__, DATA_UNKNOWN_FORMAT_TYPE, type);
 }
@@ -1410,6 +1441,16 @@ void Register::setScalarfromString(std::string stringValue)
             setValString(stringValue);
             break;
         }
+        case Bool:
+        {
+            int32_t val = 0;
+
+            if (StringUtilities::evalStringToBool(stringValue))
+                val = 1;
+
+            setValBool((int32_t)val);
+            break;
+        }
         case Date:
         {
             double val;
@@ -1478,6 +1519,16 @@ std::string Register::getValAsString(void* pValue, unsigned long i, unsigned lon
         case Float64:
             os << (double) ((double*)pValue)[index];
             break;
+        case Bool:
+        {
+            bool val = ((bool*)pValue)[index];
+            if (val)
+                os << "true";
+            else
+                os << "false";
+            
+            break;
+        }
         case String:
         {
             std::string** pStringValue = static_cast<std::string**>(pValue);
@@ -1587,6 +1638,7 @@ timeval Register::getTimeStamp()
 {
     if (isReadable())
         return tod_;
+
     throw SilecsException(__FILE__, __LINE__, PARAM_NO_TIME_STAMP, getName());
 }
 
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.h b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.h
index 3bc0838..ab9a48f 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.h
@@ -48,7 +48,8 @@ static const std::string FormatTypeString[] = { //!!Must be synchronized with Fo
 "uInt64", /*!<: 64-bits unsigned integer*/
 "Int64", /*!<: 64-bits integer*/
 "Float64", /*!<: 64-bits signed float*/
-"String" /*!<: String of ASCII characters*/
+"String", /*!<: String of ASCII characters*/
+"Bool"
 };
 
 /*!
@@ -78,8 +79,8 @@ typedef enum
     uInt64 = 8, /*!<: 64-bits unsigned integer*/
     Int64 = 9, /*!<: 64-bits integer*/
     Float64 = 10, /*!<: 64-bits signed float*/
-    String = 11
-
+    String = 11,
+    Bool = 12
 } FormatType;
 
 /*!
@@ -621,6 +622,12 @@ public:
 
     // .........................................................................
     //Recommended: from SLC6 (32bits and 64bits platform)
+    /*!
+     * \brief Extracts the current value from the register input buffer.
+     * \return the buffer content in a bool format
+     */
+    bool getValBool();
+
     /*!
      * \brief Extracts the current value from the register input buffer.
      * \return the buffer content in a int8_t format
@@ -1315,6 +1322,12 @@ public:
 
     // .........................................................................
     //Recommended: from SLC6 (32bits and 64bits platform)
+    /*!
+     * \brief Set the value for the register output buffer.(Send must be done afterwards)
+     * \param val Value to be written in the buffer in a int32_t format
+     */
+    void setValBool(int32_t val);
+
     /*!
      * \brief Set the value for the register output buffer.(Send must be done afterwards)
      * \param val Value to be written in the buffer in a int8_t format
@@ -1747,6 +1760,9 @@ protected:
     /// data address within the PLC memory (from the block base-address)
     unsigned long address_;
 
+    /// Bit number (only when format is bool)
+    unsigned long bitNumber_;
+
     /// Flag used to check if the register has been setted once at least.
     /// Uploading Slave registers at connection time is allowed only if all
     /// the retentive Slave registers are initialized!
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/utility/StringUtilities.cpp b/silecs-communication-cpp/src/silecs-communication/interface/utility/StringUtilities.cpp
index 362597c..5d7a6d6 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/utility/StringUtilities.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/utility/StringUtilities.cpp
@@ -120,4 +120,16 @@ void StringUtilities::toLower(std::string& str)
     }
 }
 
+/**
+ * Return a bool evaluation of trimmed, lowercase stringVal: 0, false, "" -> false, else true
+ */
+bool StringUtilities::evalStringToBool(std::string stringVal)
+{
+    stringVal = trim(stringVal);
+    StringUtilities::toLower(stringVal);
+    if ((stringVal == "0") || (stringVal == "false") || (stringVal == ""))
+        return false;
+    return true;
+}
+
 } // namespace
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/utility/StringUtilities.h b/silecs-communication-cpp/src/silecs-communication/interface/utility/StringUtilities.h
index 6ca5e0d..e4815ad 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/utility/StringUtilities.h
+++ b/silecs-communication-cpp/src/silecs-communication/interface/utility/StringUtilities.h
@@ -18,6 +18,10 @@ Contributors:
 #include <vector>
 #include <silecs-communication/interface/utility/SilecsException.h>
 #include <sstream>
+#include <algorithm>
+#include <functional>
+#include <cctype>
+#include <locale>
 
 namespace Silecs
 {
@@ -43,7 +47,7 @@ public:
     static std::string toString(double data);
     static std::string toString(const void * ptr);
     static void toLower(std::string& str);
-
+    static bool evalStringToBool(std::string stringVal);
     // TODO: Merge both fromString methods ( std::ios_base argument is anyhwo always the same )
     /*!
      * \brief transform a string into any integer-type
@@ -78,6 +82,29 @@ inline bool StringUtilities::from_string(T& t, const std::string& s, std::ios_ba
     return !(iss >> f >> t).fail();
 }
 
+
+/* trim from start */
+static inline std::string &ltrim(std::string &s)
+{
+    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
+    std::not1(std::ptr_fun<int, int>(std::isspace))));
+    return s;
+}
+
+/* trim from end */
+static inline std::string &rtrim(std::string &s)
+{
+    s.erase(std::find_if(s.rbegin(), s.rend(),
+        std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
+    return s;
+}
+
+/* trim from both sides */
+static inline std::string &trim(std::string &s)
+{
+    return ltrim(rtrim(s));
+}
+
 } // namespace
 
 #endif //_STRING_UTILITIES_H_
diff --git a/silecs-diagnostic-cpp/src/silecs-diagnostic/utils.cpp b/silecs-diagnostic-cpp/src/silecs-diagnostic/utils.cpp
index d796895..45cffc5 100755
--- a/silecs-diagnostic-cpp/src/silecs-diagnostic/utils.cpp
+++ b/silecs-diagnostic-cpp/src/silecs-diagnostic/utils.cpp
@@ -366,6 +366,32 @@ void Utils::displayRegisterValue(Silecs::Register *reg, QLabel *binValueLabel, Q
                     break;
                 }
 
+                case Bool:
+                {
+                    bool value = reg->getValBool();
+                    std::string textValue;
+                    std::string numericValue;
+
+                    if (value == true)
+                    {
+                        numericValue = "1";
+                        textValue = "TRUE";
+
+                    }
+                    else
+                    {
+                        numericValue = "0";
+                        textValue = "FALSE";
+                    }
+
+                    binValueLabel->setText(QString::fromStdString(numericValue));
+                    hexValueLabel->setText(QString::fromStdString(numericValue));
+                    decValueLabel->setText(QString::fromStdString(numericValue));
+                    asciiValueLabel->setText(QString::fromStdString(textValue));
+                    break;
+                }
+
+
                 default: // Float32,Float64,Date
                 {
                     binValueLabel->setText("--Not relevant--");
diff --git a/silecs-model/src/xml/DesignSchema.xsd b/silecs-model/src/xml/DesignSchema.xsd
index deda552..52ff0ca 100644
--- a/silecs-model/src/xml/DesignSchema.xsd
+++ b/silecs-model/src/xml/DesignSchema.xsd
@@ -442,6 +442,7 @@
             <xs:enumeration value="dint" />
             <xs:enumeration value="real" />
             <xs:enumeration value="dt" />
+            <xs:enumeration value="bool" />
         </xs:restriction>
     </xs:simpleType>
 
-- 
GitLab