diff --git a/silecs-codegen/src/xml/genparam.py b/silecs-codegen/src/xml/genparam.py index 12c8c4bdde56b7308061de2a116a8d6ff322f8cc..37ea5ca4f4fb549498c615ca2dc54a8a5a67912e 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 35d6d958212b26ad51cb73d128160eeed94c94a9..ab52f734a580b9e5c9eb873f5157ad9e5aa546a3 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 798081ce8d9df75328a3eb6fa7ed5639a534a332..285ef9b17dfd8a5305524f3f06973ac02bee9d36 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 0000000000000000000000000000000000000000..d22bda8a0b72e88c4de00bbc2b6a85c27cf0a024 --- /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 0000000000000000000000000000000000000000..e10f10f4ff5f7f56ec1535f7c8f983c76bbe6060 --- /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 12ed5eb92c4c5ad674b42196006f22567c51e0c2..eef367fb5ca00e7814a53ec504e9ed6ea5768255 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 86852839bbd224060498297e2dba6ae90880dc07..375a1c895bf31dbd304f5ccfa98ec6f207cbf0b7 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 3bc083824fab9af9b812be76c2fdfd3264fdc59f..ab9a48fb3c1d5b94bf0991383275ea24feac03e3 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 362597c33c0c1ade1ec0e0604e397e74a14c646f..5d7a6d632414dc7ad2cc812dfa76d7a523670e2c 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 6ca5e0de8ba625c58097eff895a0c4c87fe8c623..e4815adf2736df0848f9723a6de31bf7988e5439 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 <rim(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 d796895d3c7c2e1764ff4c9c372e094a57aad86e..45cffc5c9579f37726120176810ea2f0bbbdff67 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 deda5520152bd172dc367872a6f4db222d82951a..52ff0caeabddc0c4cbdc8edf6aaf5bedc403e0ad 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>