reworked data types
This commit is contained in:
@@ -8,6 +8,8 @@ libebus_a_SOURCES = result.cpp \
|
||||
symbol.cpp \
|
||||
symbol.h \
|
||||
filereader.h \
|
||||
datatype.cpp \
|
||||
datatype.h \
|
||||
data.cpp \
|
||||
data.h \
|
||||
device.cpp \
|
||||
|
||||
+256
-935
File diff suppressed because it is too large
Load Diff
+39
-339
@@ -22,6 +22,7 @@
|
||||
#include "symbol.h"
|
||||
#include "result.h"
|
||||
#include "filereader.h"
|
||||
#include "datatype.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
@@ -30,140 +31,22 @@
|
||||
#include <map>
|
||||
|
||||
/** @file data.h
|
||||
* Classes, functions, and constants related to decoding/encoding of symbols
|
||||
* on the eBUS to/from readable values.
|
||||
* Classes related to defining fields based on data types in symbols on the
|
||||
* eBUS.
|
||||
*
|
||||
* A @a DataField is either a @a SingleDataField or a list of
|
||||
* @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
|
||||
* @a ValueListDataField. The particular eBUS specification types like e.g.
|
||||
* @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()).
|
||||
* @a ValueListDataField.
|
||||
*
|
||||
* The @a DataFieldTemplates allow definition of derived types as well as
|
||||
* combined types based on the list of available base types. It reads the
|
||||
* instances from configuration files by inheriting the @a FileReader template
|
||||
* class.
|
||||
*/
|
||||
|
||||
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 SingleDataField;
|
||||
|
||||
@@ -349,7 +232,7 @@ public:
|
||||
* @param length the number of symbols in the message part in which the field is stored.
|
||||
*/
|
||||
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)
|
||||
: DataField(name, comment),
|
||||
m_unit(unit), m_dataType(dataType), m_partType(partType),
|
||||
@@ -361,11 +244,12 @@ public:
|
||||
virtual ~SingleDataField() {}
|
||||
|
||||
// @copydoc
|
||||
virtual SingleDataField* clone() = 0;
|
||||
virtual SingleDataField* clone();
|
||||
|
||||
/**
|
||||
* 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 name the field name.
|
||||
* @param comment the field comment.
|
||||
@@ -377,7 +261,7 @@ public:
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
* 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 PartType partType, int divisor, map<unsigned int, string> values,
|
||||
SingleDataField* &returnField);
|
||||
@@ -392,7 +276,7 @@ public:
|
||||
* Get 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.
|
||||
@@ -401,13 +285,13 @@ public:
|
||||
PartType getPartType() const { return m_partType; }
|
||||
|
||||
// @copydoc
|
||||
virtual unsigned char getLength(PartType partType, unsigned char maxLength=MAX_LEN) {
|
||||
if (partType != m_partType) {
|
||||
return (unsigned char)0;
|
||||
}
|
||||
bool remainder = m_length==REMAIN_LEN && (m_dataType.flags & ADJ)!=0;
|
||||
return remainder ? maxLength : m_length;
|
||||
}
|
||||
virtual unsigned char getLength(PartType partType, unsigned char maxLength=MAX_LEN);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t derive(string name, string comment,
|
||||
string unit, const PartType partType,
|
||||
int divisor, map<unsigned int, string> values,
|
||||
vector<SingleDataField*>& fields);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
virtual void dump(ostream& output);
|
||||
|
||||
// @copydoc
|
||||
virtual bool hasField(const char* fieldName, bool numeric);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t read(const PartType partType,
|
||||
SymbolString& data, unsigned char offset,
|
||||
@@ -438,35 +325,29 @@ public:
|
||||
|
||||
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.
|
||||
* @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 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 baseOffset,
|
||||
ostringstream& output, OutputFormat outputFormat) = 0;
|
||||
virtual result_t readSymbols(SymbolString& input,
|
||||
const unsigned char offset,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*/
|
||||
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:
|
||||
|
||||
@@ -474,7 +355,7 @@ protected:
|
||||
const string m_unit;
|
||||
|
||||
/** the data type definition. */
|
||||
const dataType_t m_dataType;
|
||||
DataType* m_dataType;
|
||||
|
||||
/** the message part in which the field is stored. */
|
||||
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.
|
||||
*/
|
||||
class ValueListDataField : public NumericDataField
|
||||
class ValueListDataField : public SingleDataField
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -681,15 +381,12 @@ public:
|
||||
* @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 values the value=text assignments.
|
||||
*/
|
||||
ValueListDataField(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 map<unsigned int, string> values)
|
||||
: NumericDataField(name, comment, unit, dataType, partType, length, bitCount,
|
||||
(unsigned char)((dataType.bitCount < 8) ? dataType.divisorOrFirstBit : 0)),
|
||||
const string unit, NumberDataType* dataType, const PartType partType,
|
||||
const unsigned char length, const map<unsigned int, string> values)
|
||||
: SingleDataField(name, comment, unit, dataType, partType, length),
|
||||
m_values(values) {}
|
||||
|
||||
/**
|
||||
@@ -712,11 +409,14 @@ public:
|
||||
protected:
|
||||
|
||||
// @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);
|
||||
|
||||
// @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:
|
||||
|
||||
|
||||
@@ -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