/******************************************************************************* | |
* Copyright (c) 2005 - 2013 Profactor GmbH, ACIN | |
* 2020 Johannes Kepler University Linz | |
* This program and the accompanying materials are made available under the | |
* terms of the Eclipse Public License 2.0 which is available at | |
* http://www.eclipse.org/legal/epl-2.0. | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* Contributors: | |
* Thomas Strasser, Ingomar Müller, Alois Zoitl, Monika Wenger, | |
* Martin Melik Merkumians | |
* - initial implementation and rework communication infrastructure | |
* Ernst Blecha - add multibit partial access | |
*******************************************************************************/ | |
#ifndef _ANY_BIT_H_ | |
#define _ANY_BIT_H_ | |
#include "forte_any_elementary.h" | |
#include "../utils/staticassert.h" | |
#include <limits> | |
/*!\ingroup COREDTS IIEC_ANY_BIT represents any bit data types according to IEC 61131. | |
*/ | |
class CIEC_ANY_BIT : public CIEC_ANY_ELEMENTARY{ | |
DECLARE_FIRMWARE_DATATYPE(ANY_BIT) | |
public: | |
virtual ~CIEC_ANY_BIT(){ | |
} | |
protected: | |
CIEC_ANY_BIT() : | |
CIEC_ANY_ELEMENTARY(){ | |
} | |
/*! \brief Set method for data type member value | |
* | |
* The data type value is set using th setLargestUInt method; | |
* This method needs to be derived in case of chaining multiple access elements after one another (e.g. [LWORD].D<1>().W<1>().B<1>().X<1>()). | |
* | |
*/ | |
virtual void setValuePartial(TLargestUIntValueType paValue) { | |
this->setLargestUInt(paValue); | |
} | |
template <class TBase, class TObject> | |
class PARTIAL_ACCESS_TYPE : public TBase { | |
protected: | |
typedef typename TBase::TValueType TBaseType; // Shortcut for the value type used by the access object | |
// evaluates to bool, TForteByte, TForteWord or TForteDWord | |
typedef typename TObject::TValueType TObjectType; // Shortcut for the value type used by the data-object | |
// evaluates to TForteByte, TForteWord, TForteDWord or TForteLWord | |
static const size_t length = std::numeric_limits<TObjectType>::digits/std::numeric_limits<TBaseType>::digits; // amount of accessible elements | |
/*! \brief Method to read the partial element form the referenced object | |
* | |
* This is a static method since it is used during object construction | |
* | |
*/ | |
static TBaseType getPartial(TObject &src, const size_t paIndex){ | |
if(paIndex >= length) { | |
return 0; // FAIL SILENT | |
} else if (forte::core::mpl::is_same<TBase,CIEC_BOOL>::value){ | |
return ( 0 != (src.getLargestUInt() & ((TLargestUIntValueType)1 << paIndex))); | |
} else { | |
TFortePartial temp; | |
temp.mLargestUInt = src.getLargestUInt(); | |
return temp.mData[paIndex]; | |
} | |
} | |
/*! \brief Method for internally setting the objects data type member value | |
* | |
* Reads the current value from the referenced object, changes the indexed element and writes it back to the referenced object. | |
* Also sets the data type member value of this object in order to allow object reuse by the compiler | |
* | |
*/ | |
PARTIAL_ACCESS_TYPE<TBase, TObject>& setPartial(TBaseType paValue){ | |
if(index < length) { | |
TFortePartial temp; | |
temp.mLargestUInt = dataObject.getLargestUInt(); | |
if (forte::core::mpl::is_same<TBase,CIEC_BOOL>::value){ | |
if(paValue) { | |
temp.mLargestUInt |= ((TLargestUIntValueType)1 << index); | |
} else { | |
temp.mLargestUInt &= ~((TLargestUIntValueType)1 << index); | |
} | |
} else { | |
temp.mData[index] = paValue; | |
} | |
dataObject.setValuePartial(static_cast<TLargestUIntValueType>(temp.mLargestUInt)); | |
TBase::setLargestUInt(paValue); // Store to the temporary object. Otherwise this might behave incorrect on object reuse. | |
} | |
return *this; | |
} | |
/*! \brief Set method for data type member value | |
* | |
* The data type value is set through the objects setPartial method; | |
* This derived method handles chaining access elements. | |
* | |
*/ | |
virtual void setValuePartial(TLargestUIntValueType paValue) { | |
setPartial(static_cast<TBaseType>(paValue)); | |
} | |
private: | |
union TFortePartial { // Split the data value into multiple indexable elements | |
TLargestUIntValueType mLargestUInt; | |
TBaseType mData[length]; | |
}; | |
TObject& dataObject; // The referenced object which data is pulled from | |
const size_t index; // Index value for accessing the split elements | |
bool accessedOutOfBounds; // Flag to indicate Out of Bounds access | |
/*! \brief Method for handling endianess conversion | |
* | |
* On little endian systems the index value can directly be returned. | |
* On big endian systems the order of bytes has to be flipped. | |
* | |
*/ | |
static size_t endianiseIndex(const size_t paIndex){ | |
#ifdef FORTE_BIG_ENDIAN | |
return (forte::core::mpl::is_same<TBase,CIEC_BOOL>::value) ? paIndex : length-1-paIndex; // Within bool-data endianess is implicitly correct | |
#else //#ifdef FORTE_BIG_ENDIAN | |
return paIndex; | |
#ifndef FORTE_LITTLE_ENDIAN | |
#error Endianess not defined | |
#endif //#ifndef FORTE_LITTLE_ENDIAN | |
#endif //#ifdef FORTE_BIG_ENDIAN | |
} | |
public: | |
/*! \brief Constructor: Setup of byte/word/dword-access-object | |
* | |
* The data member value is set based on the value of paSrc and the selected index. | |
* Out of Bounds checking is implemented as an additional runtime check. | |
* The index is stored after correction for endianess. | |
*/ | |
explicit PARTIAL_ACCESS_TYPE(TObject& paSrc, const size_t paIndex) : | |
TBase(getPartial(paSrc,endianiseIndex(paIndex))), dataObject(paSrc), index(endianiseIndex(paIndex)), | |
accessedOutOfBounds((paIndex >= length)){ | |
FORTE_STATIC_ASSERT( | |
(forte::core::mpl::is_same<TObject, CIEC_BYTE>::value || forte::core::mpl::is_same<TObject, CIEC_WORD>::value | |
|| forte::core::mpl::is_same<TObject, CIEC_DWORD>::value || forte::core::mpl::is_same<TObject, CIEC_LWORD>::value), | |
TObject_has_to_be_one_of_CIEC_BYTE_CIEC_WORD_CIEC_DWORD_or_CIEC_LWORD); | |
FORTE_STATIC_ASSERT( | |
(forte::core::mpl::is_same<TBase, CIEC_BOOL>::value || forte::core::mpl::is_same<TBase, CIEC_BYTE>::value | |
|| forte::core::mpl::is_same<TBase, CIEC_WORD>::value || forte::core::mpl::is_same<TBase, CIEC_DWORD>::value), | |
TBase_has_to_be_one_of_CIEC_BYTE_CIEC_WORD_CIEC_DWORD_or_CIEC_LWORD); | |
FORTE_STATIC_ASSERT((std::numeric_limits<TObjectType>::digits > std::numeric_limits<TBaseType>::digits), | |
Partial_access_is_only_possible_if_accessed_element_is_smaller_than_the_source); | |
}; | |
/*! \brief read the state of the Out of Bounds flag | |
* | |
*/ | |
bool getAccessedOutOfBounds() { | |
return accessedOutOfBounds; | |
} | |
/*! \brief Operator: Assignment operator with elementary type as its input | |
* | |
*/ | |
PARTIAL_ACCESS_TYPE<TBase, TObject>& operator=(TBaseType paValue){ | |
return setPartial(paValue); // No need for conversion, TBaseType can be directly assigned | |
} | |
/*! \brief Operator: Assignment operator with CIEC_XXXX as its input | |
* | |
*/ | |
PARTIAL_ACCESS_TYPE<TBase, TObject>& operator =(const TBase &paValue){ | |
return setPartial(paValue); // This does conversion from TBase to TBaseType implicitly | |
} | |
/*! \brief Operator: Assignment operator with elementary type as its input | |
* | |
*/ | |
PARTIAL_ACCESS_TYPE<TBase, TObject>& operator=(PARTIAL_ACCESS_TYPE<TBase, TObject> paValue){ | |
return setPartial((TBase)paValue); // No need for conversion, TBaseType can be directly assigned | |
} | |
/*! \brief Operator: setValue method that handles parts correctly (As long as its not a complex data structure - e.g. CIEC_STRING will not work!). | |
* | |
*/ | |
virtual void setValue(const CIEC_ANY &pa_roValue){ | |
TBase::setValue(pa_roValue); //Extract the value using the base class' setValue method | |
if (forte::core::mpl::is_same<TBase,CIEC_BOOL>::value){ | |
setPartial(static_cast<TBaseType>(false != this->getLargestUInt())); | |
} else { | |
setPartial(static_cast<TBaseType>(this->getLargestUInt())); | |
} | |
} | |
}; | |
template <class TBase, class TObject, size_t TIndex> | |
class PARTIAL_ACCESS : public CIEC_ANY_BIT::PARTIAL_ACCESS_TYPE<TBase, TObject>{ | |
public: | |
/*! \brief Constructor: Setup of byte/word/dword-access-object | |
* | |
* The data member value is set based on the value of paSrc and the selected index. | |
* Out of Bounds checking is implemented on the index value as a static check; The index therefore has to be known at compile time. | |
* The index is stored after correction for endianess. | |
*/ | |
explicit PARTIAL_ACCESS(TObject &paSrc) : | |
PARTIAL_ACCESS_TYPE<TBase, TObject>(paSrc, TIndex){ | |
FORTE_STATIC_ASSERT((TIndex<CIEC_ANY_BIT::PARTIAL_ACCESS_TYPE < TBase, TObject>::length), Index_for_partial_access_out_of_bounds); | |
}; | |
/*! \brief Operator: Assignment operator with elementary type as its input | |
* | |
*/ | |
PARTIAL_ACCESS<TBase, TObject, TIndex>& operator=(typename CIEC_ANY_BIT::PARTIAL_ACCESS_TYPE<TBase,TObject>::TBaseType paValue){ | |
this->setPartial(paValue); // No need for conversion, TBaseType can be directly assigned | |
return *this; | |
} | |
/*! \brief Operator: Assignment operator with CIEC_XXXX as its input | |
* | |
*/ | |
PARTIAL_ACCESS<TBase, TObject, TIndex>& operator =(const TBase &paValue){ | |
this->setPartial(paValue); // This does conversion from TBase to TBaseType implicitly | |
return *this; | |
} | |
}; | |
}; | |
#endif /*_MANY_BIT_H_*/ |