reworked data types

This commit is contained in:
john30
2016-06-25 06:35:07 +02:00
parent 086dea5060
commit db456abb29
5 changed files with 1649 additions and 1274 deletions
+2
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+39 -339
View File
@@ -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:
+862
View File
@@ -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);
}
+490
View File
@@ -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_