reworked data types
This commit is contained in:
@@ -8,6 +8,8 @@ libebus_a_SOURCES = result.cpp \
|
|||||||
symbol.cpp \
|
symbol.cpp \
|
||||||
symbol.h \
|
symbol.h \
|
||||||
filereader.h \
|
filereader.h \
|
||||||
|
datatype.cpp \
|
||||||
|
datatype.h \
|
||||||
data.cpp \
|
data.cpp \
|
||||||
data.h \
|
data.h \
|
||||||
device.cpp \
|
device.cpp \
|
||||||
|
|||||||
+222
-901
File diff suppressed because it is too large
Load Diff
+39
-339
@@ -22,6 +22,7 @@
|
|||||||
#include "symbol.h"
|
#include "symbol.h"
|
||||||
#include "result.h"
|
#include "result.h"
|
||||||
#include "filereader.h"
|
#include "filereader.h"
|
||||||
|
#include "datatype.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -30,140 +31,22 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
/** @file data.h
|
/** @file data.h
|
||||||
* Classes, functions, and constants related to decoding/encoding of symbols
|
* Classes related to defining fields based on data types in symbols on the
|
||||||
* on the eBUS to/from readable values.
|
* eBUS.
|
||||||
*
|
*
|
||||||
* A @a DataField is either a @a SingleDataField or a list of
|
* A @a DataField is either a @a SingleDataField or a list of
|
||||||
* @a SingleDataField instances in a @a DataFieldSet.
|
* @a SingleDataField instances in a @a DataFieldSet.
|
||||||
*
|
*
|
||||||
* A @a SingleDataField is either a string based one or a numeric one. Due to
|
|
||||||
* their text nature, date and time fields are also treated as
|
|
||||||
* @a StringDataField.
|
|
||||||
*
|
|
||||||
* The basic field types are just @a StringDataField, @a NumberDataField, and
|
* The basic field types are just @a StringDataField, @a NumberDataField, and
|
||||||
* @a ValueListDataField. The particular eBUS specification types like e.g.
|
* @a ValueListDataField.
|
||||||
* @a D1C are defined by using one of these basic field types (see #BaseType)
|
|
||||||
* with certain flags, such as #BCD, #FIX, #REQ, see #dataType_s.
|
|
||||||
*
|
|
||||||
* The list of available base types is defined an array of #dataType_s
|
|
||||||
* structures and can easily be extended if necessary.
|
|
||||||
*
|
|
||||||
* Each @a DataField can be converted from a @a SymbolString to an
|
|
||||||
* @a ostringstream (see @a DataField#read() methods) or vice versa from an
|
|
||||||
* @a istringstream to a @a SymbolString (see @a DataField#write()).
|
|
||||||
*
|
*
|
||||||
* The @a DataFieldTemplates allow definition of derived types as well as
|
* The @a DataFieldTemplates allow definition of derived types as well as
|
||||||
* combined types based on the list of available base types. It reads the
|
* combined types based on the list of available base types. It reads the
|
||||||
* instances from configuration files by inheriting the @a FileReader template
|
* instances from configuration files by inheriting the @a FileReader template
|
||||||
* class.
|
* class.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
/** the separator character used between base type name and length (in CSV only). */
|
|
||||||
#define LENGTH_SEPARATOR ':'
|
|
||||||
|
|
||||||
/** the replacement string for undefined values (in UI and CSV). */
|
|
||||||
#define NULL_VALUE "-"
|
|
||||||
|
|
||||||
/** the separator character used between fields (in UI only). */
|
|
||||||
#define UI_FIELD_SEPARATOR ';'
|
|
||||||
|
|
||||||
/** the type for data output format options. */
|
|
||||||
typedef int OutputFormat;
|
|
||||||
|
|
||||||
/* the bit flags for @a OutputFormat. */
|
|
||||||
static const unsigned int OF_VERBOSE = 0x01; //!< verbose format (names, values, units, and comments).
|
|
||||||
static const unsigned int OF_NUMERIC = 0x02; //!< numeric format (keep numeric value of value=name pairs).
|
|
||||||
static const unsigned int OF_JSON = 0x04; //!< JSON format.
|
|
||||||
|
|
||||||
/** the message part in which a data field is stored. */
|
|
||||||
enum PartType {
|
|
||||||
pt_any, //!< stored in any data (master or slave)
|
|
||||||
pt_masterData, //!< stored in master data
|
|
||||||
pt_slaveData, //!< stored in slave data
|
|
||||||
};
|
|
||||||
|
|
||||||
/** the available base data types. */
|
|
||||||
enum BaseType {
|
|
||||||
bt_str, //!< text string in a @a StringDataField
|
|
||||||
bt_hexstr, //!< hex digit string in a @a StringDataField
|
|
||||||
bt_dat, //!< date in a @a StringDataField
|
|
||||||
bt_tim, //!< time in a @a StringDataField
|
|
||||||
bt_num, //!< numeric value in a @a NumericDataField
|
|
||||||
};
|
|
||||||
|
|
||||||
/* flags for dataType_t. */
|
|
||||||
static const unsigned int ADJ = 0x01; //!< adjustable length, bitCount is maximum length
|
|
||||||
static const unsigned int BCD = 0x02; //!< binary representation is BCD
|
|
||||||
static const unsigned int REV = 0x04; //!< reverted binary representation (most significant byte first)
|
|
||||||
static const unsigned int SIG = 0x08; //!< signed value
|
|
||||||
static const unsigned int LST = 0x10; //!< value list is possible (without applied divisor)
|
|
||||||
static const unsigned int DAY = 0x20; //!< forced value list defaulting to week days
|
|
||||||
static const unsigned int IGN = 0x40; //!< ignore value during read and write
|
|
||||||
static const unsigned int FIX = 0x80; //!< fixed width formatting
|
|
||||||
static const unsigned int REQ = 0x100;//!< value may not be NULL
|
|
||||||
static const unsigned int HCD = 0x200; //!< binary representation is hex converted to decimal and interpreted as 2 digits (also requires #BCD)
|
|
||||||
static const unsigned int EXP = 0x400; //!< exponential numeric representation
|
|
||||||
|
|
||||||
/** The structure for defining data types with their properties. */
|
|
||||||
typedef struct dataType_s {
|
|
||||||
const char* name; //!< data type identifier
|
|
||||||
const unsigned char bitCount; //!< number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD)
|
|
||||||
const BaseType type; //!< base data type
|
|
||||||
const unsigned short flags; //!< flags (like #BCD)
|
|
||||||
const unsigned int replacement; //!< replacement value (fill-up value for #bt_str / #bt_hexstr, no replacement if equal to #minValue for #bt_num)
|
|
||||||
const unsigned int minValue; //!< minimum raw value (ignored for @a StringDataField)
|
|
||||||
const unsigned int maxValue; //!< maximum raw value (ignored for @a StringDataField)
|
|
||||||
const short divisorOrFirstBit; //!< #bt_num: divisor (negative for reciprocal) or offset to first bit (if (#bitCount%8)!=0)
|
|
||||||
} dataType_t;
|
|
||||||
|
|
||||||
/** the maximum allowed position within master or slave data. */
|
|
||||||
#define MAX_POS 24
|
|
||||||
|
|
||||||
/** the maximum allowed field length. */
|
|
||||||
#define MAX_LEN 31
|
|
||||||
|
|
||||||
/** the field length indicating remainder of input. */
|
|
||||||
#define REMAIN_LEN 255
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse an unsigned int value.
|
|
||||||
* @param str the string to parse.
|
|
||||||
* @param base the numerical base.
|
|
||||||
* @param minValue the minimum resulting value.
|
|
||||||
* @param maxValue the maximum resulting value.
|
|
||||||
* @param result the variable in which to store an error code when parsing failed or the value is out of bounds.
|
|
||||||
* @param length the optional variable in which to store the number of read characters.
|
|
||||||
* @return the parsed value.
|
|
||||||
*/
|
|
||||||
unsigned int parseInt(const char* str, int base, const unsigned int minValue, const unsigned int maxValue, result_t& result, unsigned int* length=NULL);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a signed int value.
|
|
||||||
* @param str the string to parse.
|
|
||||||
* @param base the numerical base.
|
|
||||||
* @param minValue the minimum resulting value.
|
|
||||||
* @param maxValue the maximum resulting value.
|
|
||||||
* @param result the variable in which to store an error code when parsing failed or the value is out of bounds.
|
|
||||||
* @param length the optional variable in which to store the number of read characters.
|
|
||||||
* @return the parsed value.
|
|
||||||
*/
|
|
||||||
int parseSignedInt(const char* str, int base, const int minValue, const int maxValue, result_t& result, unsigned int* length=NULL);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print the error position of the iterator.
|
|
||||||
* @param out the @a ostream to print to.
|
|
||||||
* @param begin the iterator to the beginning of the items.
|
|
||||||
* @param end the iterator to the end of the items.
|
|
||||||
* @param pos the iterator with the erroneous position.
|
|
||||||
* @param filename the name of the file being read.
|
|
||||||
* @param lineNo the current line number in the file being read.
|
|
||||||
* @param result the result code.
|
|
||||||
*/
|
|
||||||
void printErrorPos(ostream& out, vector<string>::iterator begin, const vector<string>::iterator end, vector<string>::iterator pos, string filename, size_t lineNo, result_t result);
|
|
||||||
|
|
||||||
|
|
||||||
class DataFieldTemplates;
|
class DataFieldTemplates;
|
||||||
class SingleDataField;
|
class SingleDataField;
|
||||||
|
|
||||||
@@ -349,7 +232,7 @@ public:
|
|||||||
* @param length the number of symbols in the message part in which the field is stored.
|
* @param length the number of symbols in the message part in which the field is stored.
|
||||||
*/
|
*/
|
||||||
SingleDataField(const string name, const string comment,
|
SingleDataField(const string name, const string comment,
|
||||||
const string unit, const dataType_t dataType, const PartType partType,
|
const string unit, DataType* dataType, const PartType partType,
|
||||||
const unsigned char length)
|
const unsigned char length)
|
||||||
: DataField(name, comment),
|
: DataField(name, comment),
|
||||||
m_unit(unit), m_dataType(dataType), m_partType(partType),
|
m_unit(unit), m_dataType(dataType), m_partType(partType),
|
||||||
@@ -361,11 +244,12 @@ public:
|
|||||||
virtual ~SingleDataField() {}
|
virtual ~SingleDataField() {}
|
||||||
|
|
||||||
// @copydoc
|
// @copydoc
|
||||||
virtual SingleDataField* clone() = 0;
|
virtual SingleDataField* clone();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method for creating a new @a SingleDataField instance derived from a base type.
|
* Factory method for creating a new @a SingleDataField instance derived from a base type.
|
||||||
* @param typeNameStr the base type name string.
|
* @param fullIdStr the full ID string (including optional length suffix).
|
||||||
|
* @param idStr the ID string (excluding optional length suffix).
|
||||||
* @param length the base type length, or 0 for default, or @a REMAIN_LEN for remainder within same message part.
|
* @param length the base type length, or 0 for default, or @a REMAIN_LEN for remainder within same message part.
|
||||||
* @param name the field name.
|
* @param name the field name.
|
||||||
* @param comment the field comment.
|
* @param comment the field comment.
|
||||||
@@ -377,7 +261,7 @@ public:
|
|||||||
* @return @a RESULT_OK on success, or an error code.
|
* @return @a RESULT_OK on success, or an error code.
|
||||||
* Note: the caller needs to free the created instance.
|
* Note: the caller needs to free the created instance.
|
||||||
*/
|
*/
|
||||||
static result_t create(const char* typeNameStr, const unsigned char length,
|
static result_t create(const char* fullIdStr, const char* idStr, const unsigned char length,
|
||||||
const string name, const string comment, const string unit,
|
const string name, const string comment, const string unit,
|
||||||
const PartType partType, int divisor, map<unsigned int, string> values,
|
const PartType partType, int divisor, map<unsigned int, string> values,
|
||||||
SingleDataField* &returnField);
|
SingleDataField* &returnField);
|
||||||
@@ -392,7 +276,7 @@ public:
|
|||||||
* Get whether this field is ignored.
|
* Get whether this field is ignored.
|
||||||
* @return whether this field is ignored.
|
* @return whether this field is ignored.
|
||||||
*/
|
*/
|
||||||
bool isIgnored() const { return (m_dataType.flags & IGN) != 0; }
|
bool isIgnored() const { return m_dataType->isIgnored(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the message part in which the field is stored.
|
* Get the message part in which the field is stored.
|
||||||
@@ -401,13 +285,13 @@ public:
|
|||||||
PartType getPartType() const { return m_partType; }
|
PartType getPartType() const { return m_partType; }
|
||||||
|
|
||||||
// @copydoc
|
// @copydoc
|
||||||
virtual unsigned char getLength(PartType partType, unsigned char maxLength=MAX_LEN) {
|
virtual unsigned char getLength(PartType partType, unsigned char maxLength=MAX_LEN);
|
||||||
if (partType != m_partType) {
|
|
||||||
return (unsigned char)0;
|
// @copydoc
|
||||||
}
|
virtual result_t derive(string name, string comment,
|
||||||
bool remainder = m_length==REMAIN_LEN && (m_dataType.flags & ADJ)!=0;
|
string unit, const PartType partType,
|
||||||
return remainder ? maxLength : m_length;
|
int divisor, map<unsigned int, string> values,
|
||||||
}
|
vector<SingleDataField*>& fields);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get whether this field uses a full byte offset.
|
* Get whether this field uses a full byte offset.
|
||||||
@@ -415,11 +299,14 @@ public:
|
|||||||
* @return true if this field uses a full byte offset, false if this field
|
* @return true if this field uses a full byte offset, false if this field
|
||||||
* only consumes a part of a byte and a subsequent field may re-use the same offset.
|
* only consumes a part of a byte and a subsequent field may re-use the same offset.
|
||||||
*/
|
*/
|
||||||
virtual bool hasFullByteOffset(bool after) { return true; }
|
virtual bool hasFullByteOffset(bool after);
|
||||||
|
|
||||||
// @copydoc
|
// @copydoc
|
||||||
virtual void dump(ostream& output);
|
virtual void dump(ostream& output);
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual bool hasField(const char* fieldName, bool numeric);
|
||||||
|
|
||||||
// @copydoc
|
// @copydoc
|
||||||
virtual result_t read(const PartType partType,
|
virtual result_t read(const PartType partType,
|
||||||
SymbolString& data, unsigned char offset,
|
SymbolString& data, unsigned char offset,
|
||||||
@@ -438,35 +325,29 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal method for reading the numeric raw value from a @a SymbolString.
|
|
||||||
* @param input the unescaped @a SymbolString to read the binary value from.
|
|
||||||
* @param offset the offset in the @a SymbolString.
|
|
||||||
* @param value the variable in which to store the numeric raw value.
|
|
||||||
* @return @a RESULT_OK on success, or an error code.
|
|
||||||
*/
|
|
||||||
virtual result_t readRawValue(SymbolString& input, const unsigned char offset, unsigned int& value) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal method for reading the field from a @a SymbolString.
|
* Internal method for reading the field from a @a SymbolString.
|
||||||
* @param input the unescaped @a SymbolString to read the binary value from.
|
* @param input the unescaped @a SymbolString to read the binary value from.
|
||||||
* @param baseOffset the base offset in the @a SymbolString.
|
* @param offset the offset in the @a SymbolString.
|
||||||
* @param output the ostringstream to append the formatted value to.
|
* @param output the ostringstream to append the formatted value to.
|
||||||
* @param outputFormat the @a OutputFormat options to use.
|
* @param outputFormat the @a OutputFormat options to use.
|
||||||
* @return @a RESULT_OK on success, or an error code.
|
* @return @a RESULT_OK on success, or an error code.
|
||||||
*/
|
*/
|
||||||
virtual result_t readSymbols(SymbolString& input, const unsigned char baseOffset,
|
virtual result_t readSymbols(SymbolString& input,
|
||||||
ostringstream& output, OutputFormat outputFormat) = 0;
|
const unsigned char offset,
|
||||||
|
ostringstream& output, OutputFormat outputFormat);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal method for writing the field to a @a SymbolString.
|
* Internal method for writing the field to a @a SymbolString.
|
||||||
* @param input the @a istringstream to parse the formatted value from.
|
* @param input the @a istringstream to parse the formatted value from.
|
||||||
* @param offset the offset in the @a SymbolString.
|
* @param offset the offset in the @a SymbolString.
|
||||||
* @param output the unescaped @a SymbolString to write the binary value to.
|
* @param output the unescaped @a SymbolString to write the binary value to.
|
||||||
* @param length the variable in which to store the used length in bytes, or NULL.
|
* @param usedLength the variable in which to store the used length in bytes, or NULL.
|
||||||
* @return @a RESULT_OK on success, or an error code.
|
* @return @a RESULT_OK on success, or an error code.
|
||||||
*/
|
*/
|
||||||
virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output, unsigned char* length) = 0;
|
virtual result_t writeSymbols(istringstream& input,
|
||||||
|
const unsigned char offset,
|
||||||
|
SymbolString& output, unsigned char* usedLength);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
@@ -474,7 +355,7 @@ protected:
|
|||||||
const string m_unit;
|
const string m_unit;
|
||||||
|
|
||||||
/** the data type definition. */
|
/** the data type definition. */
|
||||||
const dataType_t m_dataType;
|
DataType* m_dataType;
|
||||||
|
|
||||||
/** the message part in which the field is stored. */
|
/** the message part in which the field is stored. */
|
||||||
const PartType m_partType;
|
const PartType m_partType;
|
||||||
@@ -485,191 +366,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all string based data fields.
|
|
||||||
*/
|
|
||||||
class StringDataField : public SingleDataField
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new instance.
|
|
||||||
* @param name the field name.
|
|
||||||
* @param comment the field comment.
|
|
||||||
* @param unit the value unit.
|
|
||||||
* @param dataType the data type definition.
|
|
||||||
* @param partType the message part in which the field is stored.
|
|
||||||
* @param length the number of symbols in the message part in which the field is stored, or @a REMAIN_LEN for remainder within same message part.
|
|
||||||
*/
|
|
||||||
StringDataField(const string name, const string comment,
|
|
||||||
const string unit, const dataType_t dataType, const PartType partType,
|
|
||||||
const unsigned char length)
|
|
||||||
: SingleDataField(name, comment, unit, dataType, partType, length) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destructor.
|
|
||||||
*/
|
|
||||||
virtual ~StringDataField() {}
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual StringDataField* clone();
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual result_t derive(string name, string comment,
|
|
||||||
string unit, const PartType partType,
|
|
||||||
int divisor, map<unsigned int, string> values,
|
|
||||||
vector<SingleDataField*>& fields);
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual bool hasField(const char* fieldName, bool numeric);
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual void dump(ostream& output);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual result_t readRawValue(SymbolString& input, const unsigned char offset, unsigned int& value);
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual result_t readSymbols(SymbolString& input, const unsigned char baseOffset,
|
|
||||||
ostringstream& output, OutputFormat outputFormat);
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output, unsigned char* length);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all numeric data fields.
|
|
||||||
*/
|
|
||||||
class NumericDataField : public SingleDataField
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new instance.
|
|
||||||
* @param name the field name.
|
|
||||||
* @param comment the field comment.
|
|
||||||
* @param unit the value unit.
|
|
||||||
* @param dataType the data type definition.
|
|
||||||
* @param partType the message part in which the field is stored.
|
|
||||||
* @param length the number of symbols in the message part in which the field is stored.
|
|
||||||
* @param bitCount the number of bits in the binary value (may be less than @a length * 8).
|
|
||||||
* @param bitOffset the offset to the first bit in the binary value.
|
|
||||||
*/
|
|
||||||
NumericDataField(const string name, const string comment,
|
|
||||||
const string unit, const dataType_t dataType, const PartType partType,
|
|
||||||
const unsigned char length, const unsigned char bitCount, const unsigned char bitOffset)
|
|
||||||
: SingleDataField(name, comment, unit, dataType, partType, length),
|
|
||||||
m_bitCount(bitCount), m_bitOffset(bitOffset) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destructor.
|
|
||||||
*/
|
|
||||||
virtual ~NumericDataField() {}
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual NumericDataField* clone() = 0;
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual bool hasFullByteOffset(bool after);
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual bool hasField(const char* fieldName, bool numeric);
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual void dump(ostream& output);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual result_t readRawValue(SymbolString& input, const unsigned char offset, unsigned int& value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal method for writing the raw value to a @a SymbolString.
|
|
||||||
* @param value the raw value to write.
|
|
||||||
* @param offset the offset in the @a SymbolString.
|
|
||||||
* @param output the unescaped @a SymbolString to write the binary value to.
|
|
||||||
* @param length the variable in which to store the used length in bytes, or NULL.
|
|
||||||
* @return @a RESULT_OK on success, or an error code.
|
|
||||||
*/
|
|
||||||
result_t writeRawValue(unsigned int value, const unsigned char offset, SymbolString& output, unsigned char* length=NULL);
|
|
||||||
|
|
||||||
/** the number of bits in the binary value. */
|
|
||||||
const unsigned char m_bitCount;
|
|
||||||
|
|
||||||
/** the offset to the first bit in the binary value. */
|
|
||||||
const unsigned char m_bitOffset;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all numeric data fields with a number representation.
|
|
||||||
*/
|
|
||||||
class NumberDataField : public NumericDataField
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new instance.
|
|
||||||
* @param name the field name.
|
|
||||||
* @param comment the field comment.
|
|
||||||
* @param unit the value unit.
|
|
||||||
* @param dataType the data type definition.
|
|
||||||
* @param partType the message part in which the field is stored.
|
|
||||||
* @param length the number of symbols in the message part in which the field is stored.
|
|
||||||
* @param bitCount the number of bits in the binary value (may be less than @a length * 8).
|
|
||||||
* @param divisor the extra divisor (negative for reciprocal) to apply on the value, or 1 for none.
|
|
||||||
*/
|
|
||||||
NumberDataField(const string name, const string comment,
|
|
||||||
const string unit, const dataType_t dataType, const PartType partType,
|
|
||||||
const unsigned char length, const unsigned char bitCount,
|
|
||||||
const int divisor);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destructor.
|
|
||||||
*/
|
|
||||||
virtual ~NumberDataField() {}
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual NumberDataField* clone();
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual result_t derive(string name, string comment,
|
|
||||||
string unit, const PartType partType,
|
|
||||||
int divisor, map<unsigned int, string> values,
|
|
||||||
vector<SingleDataField*>& fields);
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual void dump(ostream& output);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual result_t readSymbols(SymbolString& input, const unsigned char baseOffset,
|
|
||||||
ostringstream& output, OutputFormat outputFormat);
|
|
||||||
|
|
||||||
// @copydoc
|
|
||||||
virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output, unsigned char* length);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/** the combined divisor (negative for reciprocal) to apply on the value, or 1 for none. */
|
|
||||||
const int m_divisor;
|
|
||||||
|
|
||||||
/** the precision for formatting the value. */
|
|
||||||
unsigned char m_precision;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A numeric data field with a list of value=text assignments and a string representation.
|
* A numeric data field with a list of value=text assignments and a string representation.
|
||||||
*/
|
*/
|
||||||
class ValueListDataField : public NumericDataField
|
class ValueListDataField : public SingleDataField
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -681,15 +381,12 @@ public:
|
|||||||
* @param dataType the data type definition.
|
* @param dataType the data type definition.
|
||||||
* @param partType the message part in which the field is stored.
|
* @param partType the message part in which the field is stored.
|
||||||
* @param length the number of symbols in the message part in which the field is stored.
|
* @param length the number of symbols in the message part in which the field is stored.
|
||||||
* @param bitCount the number of bits in the binary value (may be less than @a length * 8).
|
|
||||||
* @param values the value=text assignments.
|
* @param values the value=text assignments.
|
||||||
*/
|
*/
|
||||||
ValueListDataField(const string name, const string comment,
|
ValueListDataField(const string name, const string comment,
|
||||||
const string unit, const dataType_t dataType, const PartType partType,
|
const string unit, NumberDataType* dataType, const PartType partType,
|
||||||
const unsigned char length, const unsigned char bitCount,
|
const unsigned char length, const map<unsigned int, string> values)
|
||||||
const map<unsigned int, string> values)
|
: SingleDataField(name, comment, unit, dataType, partType, length),
|
||||||
: NumericDataField(name, comment, unit, dataType, partType, length, bitCount,
|
|
||||||
(unsigned char)((dataType.bitCount < 8) ? dataType.divisorOrFirstBit : 0)),
|
|
||||||
m_values(values) {}
|
m_values(values) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -712,11 +409,14 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
|
|
||||||
// @copydoc
|
// @copydoc
|
||||||
virtual result_t readSymbols(SymbolString& input, const unsigned char baseOffset,
|
virtual result_t readSymbols(SymbolString& input,
|
||||||
|
const unsigned char offset,
|
||||||
ostringstream& output, OutputFormat outputFormat);
|
ostringstream& output, OutputFormat outputFormat);
|
||||||
|
|
||||||
// @copydoc
|
// @copydoc
|
||||||
virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output, unsigned char* length);
|
virtual result_t writeSymbols(istringstream& input,
|
||||||
|
const unsigned char offset,
|
||||||
|
SymbolString& output, unsigned char* usedLength);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,862 @@
|
|||||||
|
/*
|
||||||
|
* ebusd - daemon for communication with eBUS heating systems.
|
||||||
|
* Copyright (C) 2014-2016 John Baier <ebusd@ebusd.eu>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "datatype.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstring>
|
||||||
|
#include <math.h>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
unsigned int parseInt(const char* str, int base, const unsigned int minValue, const unsigned int maxValue, result_t& result, unsigned int* length) {
|
||||||
|
char* strEnd = NULL;
|
||||||
|
|
||||||
|
unsigned long int ret = strtoul(str, &strEnd, base);
|
||||||
|
|
||||||
|
if (strEnd == NULL || strEnd == str || *strEnd != 0) {
|
||||||
|
result = RESULT_ERR_INVALID_NUM; // invalid value
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minValue > ret || ret > maxValue) {
|
||||||
|
result = RESULT_ERR_OUT_OF_RANGE; // invalid value
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (length != NULL)
|
||||||
|
*length = (unsigned int)(strEnd - str);
|
||||||
|
|
||||||
|
result = RESULT_OK;
|
||||||
|
return (unsigned int)ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parseSignedInt(const char* str, int base, const int minValue, const int maxValue, result_t& result, unsigned int* length) {
|
||||||
|
char* strEnd = NULL;
|
||||||
|
|
||||||
|
long int ret = strtol(str, &strEnd, base);
|
||||||
|
|
||||||
|
if (strEnd == NULL || *strEnd != 0) {
|
||||||
|
result = RESULT_ERR_INVALID_NUM; // invalid value
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minValue > ret || ret > maxValue) {
|
||||||
|
result = RESULT_ERR_OUT_OF_RANGE; // invalid value
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (length != NULL)
|
||||||
|
*length = (unsigned int)(strEnd - str);
|
||||||
|
|
||||||
|
result = RESULT_OK;
|
||||||
|
return (int)ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printErrorPos(ostream& out, vector<string>::iterator begin, const vector<string>::iterator end, vector<string>::iterator pos, string filename, size_t lineNo, result_t result)
|
||||||
|
{
|
||||||
|
if (pos > begin)
|
||||||
|
pos--;
|
||||||
|
out << "Error reading \"" << filename << "\" line " << setw(0) << dec << static_cast<unsigned>(lineNo) << " field " << static_cast<unsigned>(1+pos.base()-begin.base()) << " value \"" << *pos << "\": " << getResultCode(result) << endl;
|
||||||
|
out << "Erroneous item is here:" << endl;
|
||||||
|
bool first = true;
|
||||||
|
int cnt = 0;
|
||||||
|
while (begin != end) {
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else {
|
||||||
|
out << FIELD_SEPARATOR;
|
||||||
|
if (begin <= pos) {
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string item = *begin;
|
||||||
|
size_t i = item.find(TEXT_SEPARATOR);
|
||||||
|
if (i != string::npos) {
|
||||||
|
do {
|
||||||
|
item.replace(i, 1, TEXT_SEPARATOR_STR TEXT_SEPARATOR_STR);
|
||||||
|
i = item.find(TEXT_SEPARATOR, i+sizeof(TEXT_SEPARATOR_STR)+sizeof(TEXT_SEPARATOR_STR));
|
||||||
|
} while (i != string::npos);
|
||||||
|
i = 0;
|
||||||
|
} else {
|
||||||
|
i = item.find(FIELD_SEPARATOR);
|
||||||
|
}
|
||||||
|
if (i!=string::npos) {
|
||||||
|
out << TEXT_SEPARATOR << item << TEXT_SEPARATOR;
|
||||||
|
if (begin < pos)
|
||||||
|
cnt += 2;
|
||||||
|
else if (begin == pos)
|
||||||
|
cnt++;
|
||||||
|
} else {
|
||||||
|
out << item;
|
||||||
|
}
|
||||||
|
if (begin < pos)
|
||||||
|
cnt += (unsigned int)(item).length();
|
||||||
|
|
||||||
|
begin++;
|
||||||
|
}
|
||||||
|
out << endl;
|
||||||
|
out << setw(cnt) << " " << setw(0) << "^" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DataType::dump(ostream& output, const unsigned char length) const
|
||||||
|
{
|
||||||
|
output << m_id;
|
||||||
|
if (isAdjustableLength()) {
|
||||||
|
if (length==REMAIN_LEN)
|
||||||
|
output << ":*";
|
||||||
|
else
|
||||||
|
output << ":" << static_cast<unsigned>(length);
|
||||||
|
}
|
||||||
|
output << FIELD_SEPARATOR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t StringDataType::readRawValue(SymbolString& input, const unsigned char offset,
|
||||||
|
const unsigned char length, unsigned int& value)
|
||||||
|
{
|
||||||
|
return RESULT_EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t StringDataType::readSymbols(SymbolString& input, const unsigned char baseOffset,
|
||||||
|
const unsigned char length, ostringstream& output, OutputFormat outputFormat)
|
||||||
|
{
|
||||||
|
size_t start = 0, count = length;
|
||||||
|
int incr = 1;
|
||||||
|
unsigned char ch;
|
||||||
|
bool terminated = false;
|
||||||
|
if (count==REMAIN_LEN && input.size()>baseOffset) {
|
||||||
|
count = input.size()-baseOffset;
|
||||||
|
} else if (baseOffset + count > input.size()) {
|
||||||
|
return RESULT_ERR_INVALID_POS;
|
||||||
|
}
|
||||||
|
if (hasFlag(REV)) { // reverted binary representation (most significant byte first)
|
||||||
|
start = length - 1;
|
||||||
|
incr = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputFormat & OF_JSON)
|
||||||
|
output << '"';
|
||||||
|
for (size_t offset = start, i = 0; i < count; offset += incr, i++) {
|
||||||
|
ch = input[baseOffset + offset];
|
||||||
|
if (m_isHex) {
|
||||||
|
if (i > 0)
|
||||||
|
output << ' ';
|
||||||
|
output << setw(2) << hex << setfill('0') << static_cast<unsigned>(ch);
|
||||||
|
} else {
|
||||||
|
if (ch < 0x20)
|
||||||
|
ch = (unsigned char)m_replacement;
|
||||||
|
if (ch == 0x00)
|
||||||
|
terminated = true;
|
||||||
|
else if (!terminated)
|
||||||
|
output << setw(0) << dec << static_cast<char>(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (outputFormat & OF_JSON)
|
||||||
|
output << '"';
|
||||||
|
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t StringDataType::writeSymbols(istringstream& input,
|
||||||
|
unsigned char baseOffset, const unsigned char length,
|
||||||
|
SymbolString& output, unsigned char* usedLength)
|
||||||
|
{
|
||||||
|
size_t start = 0, count = length;
|
||||||
|
bool remainder = count==REMAIN_LEN && hasFlag(ADJ);
|
||||||
|
int incr = 1;
|
||||||
|
unsigned int value = 0;
|
||||||
|
string token;
|
||||||
|
|
||||||
|
if (hasFlag(REV)) { // reverted binary representation (most significant byte first)
|
||||||
|
start = length - 1;
|
||||||
|
incr = -1;
|
||||||
|
}
|
||||||
|
if (isIgnored() && !hasFlag(REQ)) {
|
||||||
|
if (remainder) {
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
for (size_t offset = start, i = 0; i < count; offset += incr, i++) {
|
||||||
|
output[baseOffset + offset] = (unsigned char)m_replacement; // fill up with replacement
|
||||||
|
}
|
||||||
|
if (usedLength!=NULL)
|
||||||
|
*usedLength = (unsigned char)count;
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
result_t result;
|
||||||
|
size_t i = 0, offset;
|
||||||
|
for (offset = start; i < count; offset += incr, i++) {
|
||||||
|
if (m_isHex) {
|
||||||
|
while (!input.eof() && input.peek() == ' ')
|
||||||
|
input.get();
|
||||||
|
if (input.eof()) { // no more digits
|
||||||
|
value = m_replacement; // fill up with replacement
|
||||||
|
} else {
|
||||||
|
token.clear();
|
||||||
|
token.push_back((unsigned char)input.get());
|
||||||
|
if (input.eof())
|
||||||
|
return RESULT_ERR_INVALID_NUM; // too short hex value
|
||||||
|
token.push_back((unsigned char)input.get());
|
||||||
|
if (input.eof())
|
||||||
|
return RESULT_ERR_INVALID_NUM; // too short hex value
|
||||||
|
|
||||||
|
value = parseInt(token.c_str(), 16, 0, 0xff, result);
|
||||||
|
if (result != RESULT_OK)
|
||||||
|
return result; // invalid hex value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (input.eof()) {
|
||||||
|
value = m_replacement;
|
||||||
|
} else {
|
||||||
|
value = input.get();
|
||||||
|
if (input.eof() || value < 0x20)
|
||||||
|
value = m_replacement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (remainder && input.eof() && i > 0) {
|
||||||
|
if (value == 0x00 && !m_isHex) {
|
||||||
|
output[baseOffset + offset] = 0;
|
||||||
|
offset += incr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (value > 0xff)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||||
|
output[baseOffset + offset] = (unsigned char)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!remainder && i < count)
|
||||||
|
return RESULT_ERR_EOF; // input too short
|
||||||
|
if (usedLength!=NULL)
|
||||||
|
*usedLength = (unsigned char)((offset-start)*incr);
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t DateTimeDataType::readRawValue(SymbolString& input, const unsigned char offset,
|
||||||
|
const unsigned char length, unsigned int& value)
|
||||||
|
{
|
||||||
|
return RESULT_EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t DateTimeDataType::readSymbols(SymbolString& input, const unsigned char baseOffset,
|
||||||
|
const unsigned char length, ostringstream& output, OutputFormat outputFormat)
|
||||||
|
{
|
||||||
|
size_t start = 0, count = length;
|
||||||
|
int incr = 1;
|
||||||
|
unsigned char ch, last = 0, hour = 0;
|
||||||
|
if (count==REMAIN_LEN && input.size()>baseOffset) {
|
||||||
|
count = input.size()-baseOffset;
|
||||||
|
} else if (baseOffset + count > input.size()) {
|
||||||
|
return RESULT_ERR_INVALID_POS;
|
||||||
|
}
|
||||||
|
if (hasFlag(REV)) { // reverted binary representation (most significant byte first)
|
||||||
|
start = length - 1;
|
||||||
|
incr = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputFormat & OF_JSON)
|
||||||
|
output << '"';
|
||||||
|
for (size_t offset = start, i = 0; i < count; offset += incr, i++) {
|
||||||
|
if (length == 4 && i == 2 && m_isDate)
|
||||||
|
continue; // skip weekday in between
|
||||||
|
ch = input[baseOffset + offset];
|
||||||
|
if (hasFlag(BCD) && (hasFlag(REQ) || ch != m_replacement)) {
|
||||||
|
if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid BCD
|
||||||
|
ch = (unsigned char)((ch >> 4) * 10 + (ch & 0x0f));
|
||||||
|
}
|
||||||
|
switch (m_isDate)
|
||||||
|
{
|
||||||
|
case true:
|
||||||
|
if (length == 2) { // number of days since 01.01.1900
|
||||||
|
if (i == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*int days = last*256 + ch + 15020; // 01.01.1900
|
||||||
|
int y = days(value < 100 ? value + 2000 : value) - 1900;
|
||||||
|
int l = (last==1 || last==2) ? 1 : 0;
|
||||||
|
int mjd = 14956 + lastLast + (int)((y-l)*365.25) + (int)((last+1+l*12)*30.6001);
|
||||||
|
int daysSinceSunday = (mjd+3) % 7; // Sun=0
|
||||||
|
if ((m_dataType.flags & BCD) != 0)
|
||||||
|
output[baseOffset + offset - incr] = (unsigned char)((6+daysSinceSunday) % 7); // Sun=0x06
|
||||||
|
else
|
||||||
|
output[baseOffset + offset - incr] = (unsigned char)(daysSinceSunday==0 ? 7 : daysSinceSunday); // Sun=0x07*/
|
||||||
|
output << setw(2) << dec << setfill('0') << static_cast<unsigned>(ch) << ".";
|
||||||
|
}
|
||||||
|
if (!hasFlag(REQ) && ch == m_replacement) {
|
||||||
|
if (i + 1 != length) {
|
||||||
|
output << NULL_VALUE << ".";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (last == m_replacement) {
|
||||||
|
output << NULL_VALUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i + 1 == length)
|
||||||
|
output << (2000 + ch);
|
||||||
|
else if (ch < 1 || (i == 0 && ch > 31) || (i == 1 && ch > 12))
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid date
|
||||||
|
else
|
||||||
|
output << setw(2) << dec << setfill('0') << static_cast<unsigned>(ch) << ".";
|
||||||
|
break;
|
||||||
|
case false:
|
||||||
|
if (!hasFlag(REQ) && ch == m_replacement) {
|
||||||
|
if (length == 1) { // truncated time
|
||||||
|
output << NULL_VALUE << ":" << NULL_VALUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i > 0)
|
||||||
|
output << ":";
|
||||||
|
output << NULL_VALUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (length == 1) { // truncated time
|
||||||
|
if (i == 0) {
|
||||||
|
ch = (unsigned char)(ch/(60/m_resolution)); // convert to hours
|
||||||
|
offset -= incr; // repeat for minutes
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ch = (unsigned char)((ch % (60/m_resolution)) * m_resolution); // convert to minutes
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
if (ch > 24)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid hour
|
||||||
|
hour = ch;
|
||||||
|
}
|
||||||
|
else if (ch > 59 || (hour == 24 && ch > 0))
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid time
|
||||||
|
if (i > 0)
|
||||||
|
output << ":";
|
||||||
|
output << setw(2) << dec << setfill('0') << static_cast<unsigned>(ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last = ch;
|
||||||
|
}
|
||||||
|
if (outputFormat & OF_JSON)
|
||||||
|
output << '"';
|
||||||
|
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t DateTimeDataType::writeSymbols(istringstream& input,
|
||||||
|
unsigned char baseOffset, const unsigned char length,
|
||||||
|
SymbolString& output, unsigned char* usedLength)
|
||||||
|
{
|
||||||
|
size_t start = 0, count = length;
|
||||||
|
bool remainder = count==REMAIN_LEN && hasFlag(ADJ);
|
||||||
|
int incr = 1;
|
||||||
|
unsigned int value = 0, last = 0, lastLast = 0;
|
||||||
|
string token;
|
||||||
|
|
||||||
|
if (hasFlag(REV)) { // reverted binary representation (most significant byte first)
|
||||||
|
start = length - 1;
|
||||||
|
incr = -1;
|
||||||
|
}
|
||||||
|
if (isIgnored() && !hasFlag(REQ)) {
|
||||||
|
if (remainder) {
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
for (size_t offset = start, i = 0; i < count; offset += incr, i++) {
|
||||||
|
output[baseOffset + offset] = (unsigned char)m_replacement; // fill up with replacement
|
||||||
|
}
|
||||||
|
if (usedLength!=NULL)
|
||||||
|
*usedLength = (unsigned char)count;
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
result_t result;
|
||||||
|
size_t i = 0, offset;
|
||||||
|
for (offset = start; i < count; offset += incr, i++) {
|
||||||
|
switch (m_isDate)
|
||||||
|
{
|
||||||
|
case true:
|
||||||
|
if (length == 4 && i == 2)
|
||||||
|
continue; // skip weekday in between
|
||||||
|
if (input.eof() || !getline(input, token, '.'))
|
||||||
|
return RESULT_ERR_EOF; // incomplete
|
||||||
|
if (!hasFlag(REQ) && strcmp(token.c_str(), NULL_VALUE) == 0) {
|
||||||
|
value = m_replacement;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
value = parseInt(token.c_str(), 10, 0, 2099, result);
|
||||||
|
if (result != RESULT_OK) {
|
||||||
|
return result; // invalid date part
|
||||||
|
}
|
||||||
|
if (i + 1 == length) {
|
||||||
|
if (length == 4) {
|
||||||
|
// calculate local week day
|
||||||
|
int y = (value < 100 ? value + 2000 : value) - 1900;
|
||||||
|
int l = (last==1 || last==2) ? 1 : 0;
|
||||||
|
int mjd = 14956 + lastLast + (int)((y-l)*365.25) + (int)((last+1+l*12)*30.6001);
|
||||||
|
int daysSinceSunday = (mjd+3) % 7; // Sun=0
|
||||||
|
if (hasFlag(BCD))
|
||||||
|
output[baseOffset + offset - incr] = (unsigned char)((6+daysSinceSunday) % 7); // Sun=0x06
|
||||||
|
else
|
||||||
|
output[baseOffset + offset - incr] = (unsigned char)(daysSinceSunday==0 ? 7 : daysSinceSunday); // Sun=0x07
|
||||||
|
}
|
||||||
|
if (value >= 2000)
|
||||||
|
value -= 2000;
|
||||||
|
if (value > 99)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid year
|
||||||
|
}
|
||||||
|
else if (value < 1 || (i == 0 && value > 31) || (i == 1 && value > 12))
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid date part
|
||||||
|
break;
|
||||||
|
case false:
|
||||||
|
if (input.eof() || !getline(input, token, LENGTH_SEPARATOR))
|
||||||
|
return RESULT_ERR_EOF; // incomplete
|
||||||
|
if (!hasFlag(REQ) && strcmp(token.c_str(), NULL_VALUE) == 0) {
|
||||||
|
value = m_replacement;
|
||||||
|
if (length == 1) { // truncated time
|
||||||
|
if (i == 0) {
|
||||||
|
last = value;
|
||||||
|
offset -= incr; // repeat for minutes
|
||||||
|
count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (last != m_replacement)
|
||||||
|
return RESULT_ERR_INVALID_NUM; // invalid truncated time minutes
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
value = parseInt(token.c_str(), 10, 0, 59, result);
|
||||||
|
if (result != RESULT_OK)
|
||||||
|
return result; // invalid time part
|
||||||
|
if ((i == 0 && value > 24) || (i > 0 && (last == 24 && value > 0) ))
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid time part
|
||||||
|
if (length == 1) { // truncated time
|
||||||
|
if (i == 0) {
|
||||||
|
last = value;
|
||||||
|
offset -= incr; // repeat for minutes
|
||||||
|
count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((value % m_resolution) != 0)
|
||||||
|
return RESULT_ERR_INVALID_NUM; // invalid truncated time minutes
|
||||||
|
value = (last * 60 + value)/m_resolution;
|
||||||
|
if (value > 24 * 6)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid time
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (remainder && input.eof() && i > 0) {
|
||||||
|
if (value == 0x00) {
|
||||||
|
output[baseOffset + offset] = 0;
|
||||||
|
offset += incr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lastLast = last;
|
||||||
|
last = value;
|
||||||
|
if (hasFlag(BCD) && (hasFlag(REQ) || value != m_replacement)) {
|
||||||
|
if (value > 99)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid BCD
|
||||||
|
value = ((value / 10) << 4) | (value % 10);
|
||||||
|
}
|
||||||
|
if (value > 0xff)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||||
|
output[baseOffset + offset] = (unsigned char)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!remainder && i < count)
|
||||||
|
return RESULT_ERR_EOF; // input too short
|
||||||
|
if (usedLength!=NULL)
|
||||||
|
*usedLength = (unsigned char)((offset-start)*incr);
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NumberDataType::NumberDataType(const char* id, const unsigned char bitCount, const unsigned short flags, const unsigned int replacement,
|
||||||
|
const unsigned int minValue, const unsigned int maxValue, const int divisor)
|
||||||
|
: DataType(id, bitCount, flags, replacement), m_minValue(minValue), m_maxValue(maxValue), m_divisor(divisor), m_firstBit(0), m_baseType(NULL)
|
||||||
|
{
|
||||||
|
unsigned char precision = 0;
|
||||||
|
if (divisor > 1)
|
||||||
|
for (unsigned int exp = 1; exp < MAX_DIVISOR; exp *= 10, precision++)
|
||||||
|
if (exp >= (unsigned int)divisor)
|
||||||
|
break;
|
||||||
|
m_precision = precision;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NumberDataType::dump(ostream& output, unsigned char length) const
|
||||||
|
{
|
||||||
|
if (m_bitCount < 8) {
|
||||||
|
DataType::dump(output, m_bitCount);
|
||||||
|
} else {
|
||||||
|
DataType::dump(output, length);
|
||||||
|
}
|
||||||
|
if (m_baseType) {
|
||||||
|
if (m_baseType->m_divisor != m_divisor) {
|
||||||
|
output << static_cast<int>(m_divisor / m_baseType->m_divisor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (m_divisor!=1) {
|
||||||
|
output << static_cast<int>(m_divisor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t NumberDataType::derive(int divisor, unsigned char bitCount, NumberDataType* &derived)
|
||||||
|
{
|
||||||
|
if (divisor == 0) {
|
||||||
|
divisor = 1;
|
||||||
|
}
|
||||||
|
if (bitCount <= 0) {
|
||||||
|
bitCount = m_bitCount;
|
||||||
|
} else if (bitCount!=m_bitCount && (m_bitCount%8)!=0) { // todo check bitCount%8=0
|
||||||
|
if (bitCount+m_firstBit>8) {
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_divisor != 1) {
|
||||||
|
if (divisor == 1) {
|
||||||
|
divisor = m_divisor;
|
||||||
|
} else if (divisor < 0) {
|
||||||
|
/*if ((divisor < 0) != (m_divisor < 0) {
|
||||||
|
return RESULT_ERR_INVALID_ARG;
|
||||||
|
}*/
|
||||||
|
if (m_divisor > 1)
|
||||||
|
return RESULT_ERR_INVALID_ARG;
|
||||||
|
|
||||||
|
divisor *= -m_divisor;
|
||||||
|
} else if (m_divisor < 0) {
|
||||||
|
if (divisor > 1)
|
||||||
|
return RESULT_ERR_INVALID_ARG;
|
||||||
|
|
||||||
|
divisor *= -m_divisor;
|
||||||
|
} else
|
||||||
|
divisor *= m_divisor;
|
||||||
|
}
|
||||||
|
if (divisor==m_divisor && bitCount==m_bitCount) {
|
||||||
|
derived = this;
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
if (-MAX_DIVISOR > divisor || divisor > MAX_DIVISOR)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
if (m_bitCount < 8)
|
||||||
|
derived = new NumberDataType(m_id, bitCount, m_flags, m_replacement,
|
||||||
|
m_firstBit, divisor);
|
||||||
|
else
|
||||||
|
derived = new NumberDataType(m_id, bitCount, m_flags, m_replacement,
|
||||||
|
m_minValue, m_maxValue, divisor);
|
||||||
|
derived->m_baseType = m_baseType ? m_baseType : this;
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t NumberDataType::readRawValue(SymbolString& input,
|
||||||
|
unsigned char baseOffset, const unsigned char length,
|
||||||
|
unsigned int& value)
|
||||||
|
{
|
||||||
|
size_t start = 0, count = length;
|
||||||
|
int incr = 1;
|
||||||
|
unsigned char ch;
|
||||||
|
|
||||||
|
if (baseOffset + length > input.size())
|
||||||
|
return RESULT_ERR_INVALID_POS; // not enough data available
|
||||||
|
|
||||||
|
if (hasFlag(REV)) { // reverted binary representation (most significant byte first)
|
||||||
|
start = length - 1;
|
||||||
|
incr = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = 0;
|
||||||
|
unsigned int exp = 1;
|
||||||
|
for (size_t offset = start, i = 0; i < count; offset += incr, i++) {
|
||||||
|
ch = input[baseOffset + offset];
|
||||||
|
if (hasFlag(BCD)) {
|
||||||
|
if (!hasFlag(REQ) && ch == (m_replacement & 0xff)) {
|
||||||
|
value = m_replacement;
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
if (!hasFlag(HCD)) {
|
||||||
|
if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid BCD
|
||||||
|
|
||||||
|
ch = (unsigned char)((ch >> 4) * 10 + (ch & 0x0f));
|
||||||
|
} else if (ch > 0x63)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid HCD
|
||||||
|
value += ch * exp;
|
||||||
|
exp *= 100;
|
||||||
|
} else {
|
||||||
|
value |= ch * exp;
|
||||||
|
exp <<= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_firstBit > 0) {
|
||||||
|
value >>= m_firstBit;
|
||||||
|
}
|
||||||
|
if (m_bitCount < 8) {
|
||||||
|
value &= (1 << m_bitCount) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t NumberDataType::readSymbols(SymbolString& input,
|
||||||
|
const unsigned char baseOffset, const unsigned char length,
|
||||||
|
ostringstream& output, OutputFormat outputFormat)
|
||||||
|
{
|
||||||
|
unsigned int value = 0;
|
||||||
|
int signedValue;
|
||||||
|
|
||||||
|
result_t result = readRawValue(input, baseOffset, length, value);
|
||||||
|
if (result != RESULT_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
output << setw(0) << dec; // initialize output
|
||||||
|
|
||||||
|
if (!hasFlag(REQ) && value == m_replacement) {
|
||||||
|
if (outputFormat & OF_JSON) {
|
||||||
|
output << "null";
|
||||||
|
} else {
|
||||||
|
output << NULL_VALUE;
|
||||||
|
}
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool negative = hasFlag(SIG) && (value & (1 << (m_bitCount - 1))) != 0;
|
||||||
|
if (m_bitCount == 32) {
|
||||||
|
if (hasFlag(EXP)) { // IEEE 754 binary32
|
||||||
|
float val;
|
||||||
|
#ifdef HAVE_DIRECT_FLOAT_FORMAT
|
||||||
|
# if HAVE_DIRECT_FLOAT_FORMAT == 2
|
||||||
|
value = __builtin_bswap32(value);
|
||||||
|
# endif
|
||||||
|
unsigned char* pval = (unsigned char*)&value;
|
||||||
|
val = *((float*)pval);
|
||||||
|
#else
|
||||||
|
int exp = (value >> 23) & 0xff; // 8 bits, signed
|
||||||
|
if (exp == 0) {
|
||||||
|
val = 0.0;
|
||||||
|
} else {
|
||||||
|
exp -= 127;
|
||||||
|
unsigned int sig = value & ((1 << 23) - 1);
|
||||||
|
val = (1.0f + (float)(sig / exp2(23))) * (float)exp2(exp);
|
||||||
|
if (negative) {
|
||||||
|
val = -val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (val != 0.0) {
|
||||||
|
if (m_divisor < 0) {
|
||||||
|
val *= (float)-m_divisor;
|
||||||
|
} else if (m_divisor > 1) {
|
||||||
|
val /= (float)m_divisor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_precision != 0) {
|
||||||
|
output << fixed << setprecision(m_precision+6);
|
||||||
|
} else if (val == 0) {
|
||||||
|
output << fixed << setprecision(1);
|
||||||
|
}
|
||||||
|
output << static_cast<double>(val);
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
if (!negative) {
|
||||||
|
if (m_divisor < 0) {
|
||||||
|
output << static_cast<float>((float)value * (float)(-m_divisor));
|
||||||
|
} else if (m_divisor <= 1) {
|
||||||
|
output << static_cast<unsigned>(value);
|
||||||
|
} else {
|
||||||
|
output << setprecision(m_precision)
|
||||||
|
<< fixed << static_cast<float>((float)value / (float)m_divisor);
|
||||||
|
}
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
signedValue = (int)value; // negative signed value
|
||||||
|
} else if (negative) { // negative signed value
|
||||||
|
signedValue = (int)value - (1 << m_bitCount);
|
||||||
|
} else {
|
||||||
|
signedValue = (int)value;
|
||||||
|
}
|
||||||
|
if (m_divisor < 0) {
|
||||||
|
output << static_cast<float>((float)signedValue * (float)(-m_divisor));
|
||||||
|
} else if (m_divisor <= 1) {
|
||||||
|
if (hasFlag(FIX) && hasFlag(BCD)) {
|
||||||
|
if (outputFormat & OF_JSON) {
|
||||||
|
output << '"';
|
||||||
|
output << setw(length * 2) << setfill('0');
|
||||||
|
output << static_cast<signed>(signedValue) << setw(0);
|
||||||
|
output << '"';
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
output << setw(length * 2) << setfill('0');
|
||||||
|
}
|
||||||
|
output << static_cast<signed>(signedValue) << setw(0);
|
||||||
|
} else {
|
||||||
|
output << setprecision(m_precision)
|
||||||
|
<< fixed << static_cast<float>((float)signedValue / (float)m_divisor);
|
||||||
|
}
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t NumberDataType::writeRawValue(unsigned int value,
|
||||||
|
const unsigned char baseOffset, const unsigned char length,
|
||||||
|
SymbolString& output, unsigned char* usedLength)
|
||||||
|
{
|
||||||
|
size_t start = 0, count = length;
|
||||||
|
int incr = 1;
|
||||||
|
unsigned char ch;
|
||||||
|
|
||||||
|
if (m_bitCount < 8 && (value & ~((1 << m_bitCount) - 1)) != 0) {
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE;
|
||||||
|
}
|
||||||
|
if (m_firstBit > 0) {
|
||||||
|
value <<= m_firstBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasFlag(REV)) { // reverted binary representation (most significant byte first)
|
||||||
|
start = length - 1;
|
||||||
|
incr = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t offset = start, i = 0, exp = 1; i < count; offset += incr, i++) {
|
||||||
|
if (hasFlag(BCD)) {
|
||||||
|
if (!hasFlag(REQ) && value == m_replacement)
|
||||||
|
ch = m_replacement & 0xff;
|
||||||
|
else {
|
||||||
|
ch = (unsigned char)((value / exp) % 100);
|
||||||
|
if (!hasFlag(HCD))
|
||||||
|
ch = (unsigned char)(((ch / 10) << 4) | (ch % 10));
|
||||||
|
}
|
||||||
|
exp *= 100;
|
||||||
|
} else {
|
||||||
|
ch = (value / exp) & 0xff;
|
||||||
|
exp <<= 8;
|
||||||
|
}
|
||||||
|
if (offset == start && (m_bitCount % 8) != 0 && baseOffset + offset < output.size())
|
||||||
|
output[baseOffset + offset] |= ch;
|
||||||
|
else
|
||||||
|
output[baseOffset + offset] = ch;
|
||||||
|
}
|
||||||
|
if (usedLength!=NULL)
|
||||||
|
*usedLength = length;
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t NumberDataType::writeSymbols(istringstream& input,
|
||||||
|
const unsigned char baseOffset, const unsigned char length,
|
||||||
|
SymbolString& output, unsigned char* usedLength)
|
||||||
|
{
|
||||||
|
unsigned int value;
|
||||||
|
|
||||||
|
const char* str = input.str().c_str();
|
||||||
|
if (!hasFlag(REQ) && (isIgnored() || strcasecmp(str, NULL_VALUE) == 0)) {
|
||||||
|
value = m_replacement; // replacement value
|
||||||
|
} else if (str == NULL || *str == 0) {
|
||||||
|
return RESULT_ERR_EOF; // input too short
|
||||||
|
} else if (hasFlag(EXP)) { // IEEE 754 binary32
|
||||||
|
char* strEnd = NULL;
|
||||||
|
double dvalue = strtod(str, &strEnd);
|
||||||
|
if (strEnd == NULL || strEnd == str || *strEnd != 0) {
|
||||||
|
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||||
|
}
|
||||||
|
if (m_divisor < 0)
|
||||||
|
dvalue /= -m_divisor;
|
||||||
|
else if (m_divisor > 1)
|
||||||
|
dvalue *= m_divisor;
|
||||||
|
#ifdef HAVE_DIRECT_FLOAT_FORMAT
|
||||||
|
float val = (float)dvalue;
|
||||||
|
unsigned char* pval = (unsigned char*)&val;
|
||||||
|
value = *((int32_t*)pval);
|
||||||
|
# if HAVE_DIRECT_FLOAT_FORMAT == 2
|
||||||
|
value = __builtin_bswap32(value);
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
value = 0;
|
||||||
|
if (dvalue != 0) {
|
||||||
|
bool negative = dvalue < 0;
|
||||||
|
if (negative) {
|
||||||
|
dvalue = -dvalue;
|
||||||
|
}
|
||||||
|
int exp = ilogb(dvalue);
|
||||||
|
if (exp < -126 || exp > 127)
|
||||||
|
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||||
|
dvalue = scalbln(dvalue, -exp) - 1.0;
|
||||||
|
unsigned int sig = (unsigned int)(dvalue * exp2(23));
|
||||||
|
exp += 127;
|
||||||
|
value = (exp << 23) | sig;
|
||||||
|
if (negative) {
|
||||||
|
value |= 0x80000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
char* strEnd = NULL;
|
||||||
|
if (m_divisor == 1) {
|
||||||
|
if (hasFlag(SIG)) {
|
||||||
|
long int signedValue = strtol(str, &strEnd, 10);
|
||||||
|
if (signedValue < 0 && m_bitCount != 32)
|
||||||
|
value = (unsigned int)(signedValue + (1 << m_bitCount));
|
||||||
|
else
|
||||||
|
value = (unsigned int)signedValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
value = (unsigned int)strtoul(str, &strEnd, 10);
|
||||||
|
if (strEnd == NULL || strEnd == str || (*strEnd != 0 && *strEnd != '.'))
|
||||||
|
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||||
|
} else {
|
||||||
|
char* strEnd = NULL;
|
||||||
|
double dvalue = strtod(str, &strEnd);
|
||||||
|
if (strEnd == NULL || strEnd == str || *strEnd != 0)
|
||||||
|
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||||
|
if (m_divisor < 0)
|
||||||
|
dvalue = round(dvalue / -m_divisor);
|
||||||
|
else
|
||||||
|
dvalue = round(dvalue * m_divisor);
|
||||||
|
if (hasFlag(SIG)) {
|
||||||
|
if (dvalue < -(1LL << (8 * length)) || dvalue >= (1LL << (8 * length)))
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||||
|
if (dvalue < 0 && m_bitCount != 32)
|
||||||
|
value = (int)(dvalue + (1 << m_bitCount));
|
||||||
|
else
|
||||||
|
value = (int)dvalue;
|
||||||
|
} else {
|
||||||
|
if (dvalue < 0.0 || dvalue >= (1LL << (8 * length)))
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||||
|
value = (unsigned int)dvalue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasFlag(SIG)) { // signed value
|
||||||
|
if ((value & (1 << (m_bitCount - 1))) != 0) { // negative signed value
|
||||||
|
if (value < m_minValue)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||||
|
}
|
||||||
|
else if (value > m_maxValue)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||||
|
}
|
||||||
|
else if (value < m_minValue || value > m_maxValue)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeRawValue(value, baseOffset, length, output, usedLength);
|
||||||
|
}
|
||||||
@@ -0,0 +1,490 @@
|
|||||||
|
/*
|
||||||
|
* ebusd - daemon for communication with eBUS heating systems.
|
||||||
|
* Copyright (C) 2014-2016 John Baier <ebusd@ebusd.eu>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBEBUS_DATATYPE_H_
|
||||||
|
#define LIBEBUS_DATATYPE_H_
|
||||||
|
|
||||||
|
#include "symbol.h"
|
||||||
|
#include "result.h"
|
||||||
|
#include "filereader.h"
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
/** @file datatype.h
|
||||||
|
* Classes, functions, and constants related to decoding/encoding of symbols
|
||||||
|
* on the eBUS to/from readable values and registry of data types.
|
||||||
|
*
|
||||||
|
* A @a DataType is one of @a StringDataType, @a DateTimeDataType,
|
||||||
|
* @a NumberDataType, or @a BitsDataType.
|
||||||
|
*
|
||||||
|
* The particular eBUS specification types like e.g. @a D1C are defined by
|
||||||
|
* using one of these base data types with certain flags, such as #BCD, #FIX,
|
||||||
|
* #REQ, see @a DataType.
|
||||||
|
*
|
||||||
|
* The list of available base data types is stored in #baseTypes and can easily
|
||||||
|
* be extended if necessary.
|
||||||
|
*
|
||||||
|
* Each @a DataType can be converted from a @a SymbolString to an
|
||||||
|
* @a ostringstream (see @a DataType#readSymbols() methods) or vice versa from
|
||||||
|
* an @a istringstream to a @a SymbolString (see @a DataType#writeSymbols()).
|
||||||
|
*/
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
/** the separator character used between base type name and length (in CSV only). */
|
||||||
|
#define LENGTH_SEPARATOR ':'
|
||||||
|
|
||||||
|
/** the replacement string for undefined values (in UI and CSV). */
|
||||||
|
#define NULL_VALUE "-"
|
||||||
|
|
||||||
|
/** the separator character used between fields (in UI only). */
|
||||||
|
#define UI_FIELD_SEPARATOR ';'
|
||||||
|
|
||||||
|
/** the maximum allowed position within master or slave data. */
|
||||||
|
#define MAX_POS 24
|
||||||
|
|
||||||
|
/** the maximum allowed field length. */
|
||||||
|
#define MAX_LEN 31
|
||||||
|
|
||||||
|
/** the field length indicating remainder of input. */
|
||||||
|
#define REMAIN_LEN 255
|
||||||
|
|
||||||
|
/** the maximum divisor value. */
|
||||||
|
#define MAX_DIVISOR 1000000000
|
||||||
|
|
||||||
|
/** the maximum value for value lists. */
|
||||||
|
#define MAX_VALUE (0xFFFFFFFFu)
|
||||||
|
|
||||||
|
|
||||||
|
/** the type for data output format options. */
|
||||||
|
typedef int OutputFormat;
|
||||||
|
|
||||||
|
/* the bit flags for @a OutputFormat. */
|
||||||
|
static const unsigned int OF_VERBOSE = 0x01; //!< verbose format (names, values, units, and comments).
|
||||||
|
static const unsigned int OF_NUMERIC = 0x02; //!< numeric format (keep numeric value of value=name pairs).
|
||||||
|
static const unsigned int OF_JSON = 0x04; //!< JSON format.
|
||||||
|
|
||||||
|
/** the message part in which a data field is stored. */
|
||||||
|
enum PartType {
|
||||||
|
pt_any, //!< stored in any data (master or slave)
|
||||||
|
pt_masterData, //!< stored in master data
|
||||||
|
pt_slaveData, //!< stored in slave data
|
||||||
|
};
|
||||||
|
|
||||||
|
/* flags for @a DataType. */
|
||||||
|
static const unsigned int ADJ = 0x01; //!< adjustable length, bitCount is maximum length
|
||||||
|
static const unsigned int BCD = 0x02; //!< binary representation is BCD
|
||||||
|
static const unsigned int REV = 0x04; //!< reverted binary representation (most significant byte first)
|
||||||
|
static const unsigned int SIG = 0x08; //!< signed value
|
||||||
|
static const unsigned int IGN = 0x10; //!< ignore value during read and write
|
||||||
|
static const unsigned int FIX = 0x20; //!< fixed width formatting
|
||||||
|
static const unsigned int REQ = 0x40;//!< value may not be NULL
|
||||||
|
static const unsigned int HCD = 0x80; //!< binary representation is hex converted to decimal and interpreted as 2 digits (also requires #BCD)
|
||||||
|
static const unsigned int EXP = 0x100; //!< exponential numeric representation
|
||||||
|
static const unsigned int LAST_DATATYPE_FLAG = EXP; //!< the last flag value used by @a DataType and children
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an unsigned int value.
|
||||||
|
* @param str the string to parse.
|
||||||
|
* @param base the numerical base.
|
||||||
|
* @param minValue the minimum resulting value.
|
||||||
|
* @param maxValue the maximum resulting value.
|
||||||
|
* @param result the variable in which to store an error code when parsing failed or the value is out of bounds.
|
||||||
|
* @param length the optional variable in which to store the number of read characters.
|
||||||
|
* @return the parsed value.
|
||||||
|
*/
|
||||||
|
unsigned int parseInt(const char* str, int base, const unsigned int minValue, const unsigned int maxValue, result_t& result, unsigned int* length=NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a signed int value.
|
||||||
|
* @param str the string to parse.
|
||||||
|
* @param base the numerical base.
|
||||||
|
* @param minValue the minimum resulting value.
|
||||||
|
* @param maxValue the maximum resulting value.
|
||||||
|
* @param result the variable in which to store an error code when parsing failed or the value is out of bounds.
|
||||||
|
* @param length the optional variable in which to store the number of read characters.
|
||||||
|
* @return the parsed value.
|
||||||
|
*/
|
||||||
|
int parseSignedInt(const char* str, int base, const int minValue, const int maxValue, result_t& result, unsigned int* length=NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the error position of the iterator.
|
||||||
|
* @param out the @a ostream to print to.
|
||||||
|
* @param begin the iterator to the beginning of the items.
|
||||||
|
* @param end the iterator to the end of the items.
|
||||||
|
* @param pos the iterator with the erroneous position.
|
||||||
|
* @param filename the name of the file being read.
|
||||||
|
* @param lineNo the current line number in the file being read.
|
||||||
|
* @param result the result code.
|
||||||
|
*/
|
||||||
|
void printErrorPos(ostream& out, vector<string>::iterator begin, const vector<string>::iterator end, vector<string>::iterator pos, string filename, size_t lineNo, result_t result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all kinds of data types.
|
||||||
|
*/
|
||||||
|
class DataType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance.
|
||||||
|
* @param id the type identifier.
|
||||||
|
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
|
||||||
|
* @param flags the combination of flags (like #BCD).
|
||||||
|
* @param replacement the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue).
|
||||||
|
*/
|
||||||
|
DataType(const char* id, const unsigned char bitCount, const unsigned short flags, const unsigned int replacement)
|
||||||
|
: m_id(id), m_bitCount(bitCount), m_flags(flags), m_replacement(replacement) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataType() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the type identifier.
|
||||||
|
*/
|
||||||
|
string getId() const { return m_id; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the number of bits (maximum length if #ADJ flag is set).
|
||||||
|
*/
|
||||||
|
unsigned char getBitCount() const { return m_bitCount; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a flag is set.
|
||||||
|
* @param flag the flag to check (like #BCD).
|
||||||
|
* @return whether the flag is set.
|
||||||
|
*/
|
||||||
|
bool hasFlag(const unsigned int flag) const { return (m_flags & flag) != 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this field is ignored.
|
||||||
|
*/
|
||||||
|
bool isIgnored() const { return hasFlag(IGN); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this field has an .
|
||||||
|
*/
|
||||||
|
bool isAdjustableLength() const { return hasFlag(ADJ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue).
|
||||||
|
*/
|
||||||
|
unsigned int getReplacement() const { return m_replacement; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump the type identifier with the specified length and optionally the
|
||||||
|
* divisor to the output.
|
||||||
|
* @param output the @a ostream to dump to.
|
||||||
|
* @param length the number of symbols to read/write.
|
||||||
|
* @return true when a non-default divisor was written to the output.
|
||||||
|
*/
|
||||||
|
virtual bool dump(ostream& output, const unsigned char length) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method for reading the numeric raw value from a @a SymbolString.
|
||||||
|
* @param input the unescaped @a SymbolString to read the binary value from.
|
||||||
|
* @param offset the offset in the @a SymbolString.
|
||||||
|
* @param length the number of symbols to read.
|
||||||
|
* @param value the variable in which to store the numeric raw value.
|
||||||
|
* @return @a RESULT_OK on success, or an error code.
|
||||||
|
*/
|
||||||
|
virtual result_t readRawValue(SymbolString& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
unsigned int& value) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method for reading the field from a @a SymbolString.
|
||||||
|
* @param input the unescaped @a SymbolString to read the binary value from.
|
||||||
|
* @param offset the offset in the @a SymbolString.
|
||||||
|
* @param length the number of symbols to read.
|
||||||
|
* @param output the ostringstream to append the formatted value to.
|
||||||
|
* @param outputFormat the @a OutputFormat options to use.
|
||||||
|
* @return @a RESULT_OK on success, or an error code.
|
||||||
|
*/
|
||||||
|
virtual result_t readSymbols(SymbolString& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
ostringstream& output, OutputFormat outputFormat) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method for writing the field to a @a SymbolString.
|
||||||
|
* @param input the @a istringstream to parse the formatted value from.
|
||||||
|
* @param offset the offset in the @a SymbolString.
|
||||||
|
* @param length the number of symbols to write, or @a REMAIN_LEN.
|
||||||
|
* @param output the unescaped @a SymbolString to write the binary value to.
|
||||||
|
* @param usedLength the variable in which to store the used length in bytes, or NULL.
|
||||||
|
* @return @a RESULT_OK on success, or an error code.
|
||||||
|
*/
|
||||||
|
virtual result_t writeSymbols(istringstream& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
SymbolString& output, unsigned char* usedLength) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/** the type identifier. */
|
||||||
|
const char* m_id;
|
||||||
|
|
||||||
|
/** the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). */
|
||||||
|
const unsigned char m_bitCount;
|
||||||
|
|
||||||
|
/** the combination of flags (like #BCD). */
|
||||||
|
const unsigned short m_flags;
|
||||||
|
|
||||||
|
/** the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue). */
|
||||||
|
const unsigned int m_replacement;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string based @a DataType.
|
||||||
|
*/
|
||||||
|
class StringDataType : public DataType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance.
|
||||||
|
* @param id the type identifier.
|
||||||
|
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
|
||||||
|
* @param flags the combination of flags (like #BCD).
|
||||||
|
* @param replacement the replacement value (fill-up value).
|
||||||
|
* @param isHex true for hex digits instead of characters.
|
||||||
|
*/
|
||||||
|
StringDataType(const char* id, const unsigned char bitCount, const unsigned short flags,
|
||||||
|
const unsigned int replacement, bool isHex=false)
|
||||||
|
: DataType(id, bitCount, flags, replacement), m_isHex(isHex) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~StringDataType() {}
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual result_t readRawValue(SymbolString& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
unsigned int& value);
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual result_t readSymbols(SymbolString& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
ostringstream& output, OutputFormat outputFormat);
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual result_t writeSymbols(istringstream& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
SymbolString& output, unsigned char* usedLength);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** true for hex digits instead of characters. */
|
||||||
|
const bool m_isHex;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A date/time based @a DataType.
|
||||||
|
*/
|
||||||
|
class DateTimeDataType : public DataType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance.
|
||||||
|
* @param id the type identifier.
|
||||||
|
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
|
||||||
|
* @param flags the combination of flags (like #BCD).
|
||||||
|
* @param replacement the replacement value.
|
||||||
|
* @param isDate true for date, false for time.
|
||||||
|
* @param resolution the the resolution in minutes for time types, or 1.
|
||||||
|
*/
|
||||||
|
DateTimeDataType(const char* id, const unsigned char bitCount, const unsigned short flags, const unsigned int replacement,
|
||||||
|
const bool isDate, const short resolution)
|
||||||
|
: DataType(id, bitCount, flags, replacement), m_isDate(isDate), m_resolution(resolution) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DateTimeDataType() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true for date, false for time.
|
||||||
|
*/
|
||||||
|
bool isDate() const { return m_isDate; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the resolution in minutes for time types, or 1.
|
||||||
|
*/
|
||||||
|
short getResolution() const { return m_resolution; }
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual result_t readRawValue(SymbolString& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
unsigned int& value);
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual result_t readSymbols(SymbolString& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
ostringstream& output, OutputFormat outputFormat);
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual result_t writeSymbols(istringstream& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
SymbolString& output, unsigned char* usedLength);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** true for date, false for time. */
|
||||||
|
const bool m_isDate;
|
||||||
|
|
||||||
|
/** the resolution in minutes for time types, or 1. */
|
||||||
|
const short m_resolution;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for numeric @a DataType.
|
||||||
|
*/
|
||||||
|
class NumberDataType : public DataType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance for multiple of 8 bits.
|
||||||
|
* @param id the type identifier.
|
||||||
|
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
|
||||||
|
* @param flags the combination of flags (like #BCD).
|
||||||
|
* @param replacement the replacement value (no replacement if equal to #minValue).
|
||||||
|
* @param minValue the minimum raw value.
|
||||||
|
* @param maxValue the maximum raw value.
|
||||||
|
* @param divisor the divisor (negative for reciprocal).
|
||||||
|
*/
|
||||||
|
NumberDataType(const char* id, const unsigned char bitCount, const unsigned short flags, const unsigned int replacement,
|
||||||
|
const unsigned int minValue, const unsigned int maxValue, const int divisor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance for less than 8 bits.
|
||||||
|
* @param id the type identifier.
|
||||||
|
* @param bitCount the number of bits (maximum length if #ADJ flag is set).
|
||||||
|
* @param flags the combination of flags (like #ADJ, must not include flag #BCD).
|
||||||
|
* @param replacement the replacement value (no replacement if zero).
|
||||||
|
* @param firstBit the offset to the first bit.
|
||||||
|
* @param divisor the divisor (negative for reciprocal).
|
||||||
|
*/
|
||||||
|
NumberDataType(const char* id, const unsigned char bitCount, const unsigned short flags, const unsigned int replacement,
|
||||||
|
const short firstBit, const int divisor)
|
||||||
|
: DataType(id, bitCount, flags, replacement), m_minValue(0), m_maxValue((1<<bitCount)-1), m_divisor(divisor), m_precision(0), m_firstBit(firstBit), m_baseType(NULL) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~NumberDataType() {}
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual bool dump(ostream& output, const unsigned char length) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive a new @a NumberDataType from this.
|
||||||
|
* @param divisor the extra divisor (negative for reciprocal) to apply, or 1 for none (if applicable).
|
||||||
|
* @param derived the derived @a NumberDataType, or this if derivation is not necessary.
|
||||||
|
* @return @a RESULT_OK on success, or an error code.
|
||||||
|
*/
|
||||||
|
result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the minimum raw value.
|
||||||
|
*/
|
||||||
|
unsigned int getMinValue() const { return m_minValue; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maximum raw value.
|
||||||
|
*/
|
||||||
|
unsigned int getMaxValue() const { return m_maxValue; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the divisor (negative for reciprocal).
|
||||||
|
*/
|
||||||
|
int getDivisor() const { return m_divisor; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the precision for formatting the value.
|
||||||
|
*/
|
||||||
|
unsigned char getPrecision() const { return m_precision; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the offset to the first bit.
|
||||||
|
*/
|
||||||
|
short getFirstBit() const { return m_firstBit; }
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual result_t readRawValue(SymbolString& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
unsigned int& value);
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual result_t readSymbols(SymbolString& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
ostringstream& output, OutputFormat outputFormat);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method for writing the numeric raw value to a @a SymbolString.
|
||||||
|
* @param value the numeric raw value to write.
|
||||||
|
* @param offset the offset in the @a SymbolString.
|
||||||
|
* @param length the number of symbols to write, or @a REMAIN_LEN.
|
||||||
|
* @param output the unescaped @a SymbolString to write the binary value to.
|
||||||
|
* @param length the variable in which to store the used length in bytes, or NULL.
|
||||||
|
* @return @a RESULT_OK on success, or an error code.
|
||||||
|
*/
|
||||||
|
virtual result_t writeRawValue(unsigned int value,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
SymbolString& output, unsigned char* usedLength=NULL);
|
||||||
|
|
||||||
|
// @copydoc
|
||||||
|
virtual result_t writeSymbols(istringstream& input,
|
||||||
|
const unsigned char offset, const unsigned char length,
|
||||||
|
SymbolString& output, unsigned char* usedLength);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** the minimum raw value. */
|
||||||
|
const unsigned int m_minValue;
|
||||||
|
|
||||||
|
/** the maximum raw value. */
|
||||||
|
const unsigned int m_maxValue;
|
||||||
|
|
||||||
|
/** the divisor (negative for reciprocal). */
|
||||||
|
const int m_divisor;
|
||||||
|
|
||||||
|
/** the precision for formatting the value. */
|
||||||
|
unsigned char m_precision;
|
||||||
|
|
||||||
|
/** the offset to the first bit. */
|
||||||
|
const short m_firstBit;
|
||||||
|
|
||||||
|
/** the base @a NumberDataType for derived instances. */
|
||||||
|
NumberDataType* m_baseType;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LIBEBUS_DATATYPE_H_
|
||||||
Reference in New Issue
Block a user