blob: d6eed3248dfda9064233c0dfe7540c609e73da59 [file] [log] [blame]
/*******************************************************************************
* 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_*/