diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index d0c0fbf9..cb7e5ca3 100644 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -101,7 +101,7 @@ unsigned int parseInt(const char* str, int base, const unsigned int minValue, co return 0; } - if (ret < minValue || ret > maxValue) { + if (minValue > ret || ret > maxValue) { result = RESULT_ERR_OUT_OF_RANGE; // invalid value return 0; } @@ -112,6 +112,27 @@ unsigned int parseInt(const char* str, int base, const unsigned int minValue, co 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(vector::iterator begin, const vector::iterator end, vector::iterator pos, string filename, size_t lineNo, result_t result) { if (pos > begin) @@ -157,7 +178,7 @@ result_t DataField::create(vector::iterator& it, do { string unit, comment; PartType partType; - unsigned int divisor = 0; + int divisor = 0; const bool isTemplate = dstAddress == SYN; bool hasPartStr = false; string token; @@ -210,7 +231,7 @@ result_t DataField::create(vector::iterator& it, const string divisorStr = *it++; if (!divisorStr.empty()) { if (divisorStr.find('=') == string::npos) - divisor = parseInt(divisorStr.c_str(), 10, 1, MAX_DIVISOR, result); + divisor = parseSignedInt(divisorStr.c_str(), 10, -MAX_DIVISOR, MAX_DIVISOR, result); else { istringstream stream(divisorStr); while (getline(stream, token, VALUE_SEPARATOR) != 0) { @@ -337,9 +358,26 @@ result_t DataField::create(vector::iterator& it, if (values.empty() || (dataType->flags & LST) == 0) { if (divisor == 0) divisor = 1; + if ((dataType->bitCount % 8) == 0) { - divisor *= dataType->divisorOrFirstBit; - if (divisor > MAX_DIVISOR) { + if (divisor < 0) { + if (dataType->divisorOrFirstBit > 1) { + result = RESULT_ERR_INVALID_ARG; + break; + } + if (dataType->divisorOrFirstBit < 0) + divisor *= -dataType->divisorOrFirstBit; + } else if (dataType->divisorOrFirstBit < 0) { + if (divisor > 1) { + result = RESULT_ERR_INVALID_ARG; + break; + } + if (divisor < 0) + divisor *= -dataType->divisorOrFirstBit; + } else + divisor *= dataType->divisorOrFirstBit; + + if (-MAX_DIVISOR > divisor || divisor > MAX_DIVISOR) { result = RESULT_ERR_OUT_OF_RANGE; break; } @@ -475,7 +513,7 @@ result_t SingleDataField::write(istringstream& input, result_t StringDataField::derive(string name, string comment, string unit, const PartType partType, - unsigned int divisor, map values, + int divisor, map values, vector& fields) { if (m_partType != pt_any && partType == pt_any) @@ -852,19 +890,20 @@ result_t NumericDataField::writeRawValue(unsigned int value, NumberDataField::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 unsigned int divisor) + const int divisor) : NumericDataField(name, comment, unit, dataType, partType, length, bitCount, (dataType.bitCount % 8) == 0 ? 0 : (unsigned char)dataType.divisorOrFirstBit), m_divisor(divisor), m_precision(0) { - for (unsigned int exp = 1; exp < MAX_DIVISOR; exp *= 10, m_precision++) - if (exp >= divisor) - break; + if (divisor > 1) + for (unsigned int exp = 1; exp < MAX_DIVISOR; exp *= 10, m_precision++) + if (exp >= (unsigned int)divisor) + break; } result_t NumberDataField::derive(string name, string comment, string unit, const PartType partType, - unsigned int divisor, map values, + int divisor, map values, vector& fields) { if (m_partType != pt_any && partType == pt_any) @@ -875,23 +914,37 @@ result_t NumberDataField::derive(string name, string comment, comment = m_comment; if (unit.empty()) unit = m_unit; - if (divisor == 0) - divisor = m_divisor; - else if ((m_dataType.bitCount % 8) == 0) { - divisor *= m_dataType.divisorOrFirstBit; - if (divisor > MAX_DIVISOR) { - return RESULT_ERR_OUT_OF_RANGE; - } - } if (!values.empty()) { - if (divisor != 1) + if (divisor != 0 || m_divisor != 1) return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field fields.push_back(new ValueListDataField(name, comment, unit, m_dataType, partType, m_length, m_bitCount, values)); } - else - fields.push_back(new NumberDataField(name, comment, unit, m_dataType, partType, m_length, m_bitCount, divisor)); + else { + if (divisor == 0) + divisor = m_divisor; + else if ((m_dataType.bitCount % 8) == 0) { + if (divisor < 0) { + if (m_divisor > 1) + return RESULT_ERR_INVALID_ARG; + if (m_divisor < 0) + divisor *= -m_divisor; + } else if (m_divisor < 0) { + if (divisor > 1) + return RESULT_ERR_INVALID_ARG; + + if (divisor < 0) + divisor *= -m_divisor; + } else + divisor *= m_divisor; + + if (-MAX_DIVISOR > divisor || divisor > MAX_DIVISOR) { + return RESULT_ERR_OUT_OF_RANGE; + } + } + fields.push_back(new NumberDataField(name, comment, unit, m_dataType, partType, m_length, m_bitCount, divisor)); + } return RESULT_OK; } @@ -924,7 +977,9 @@ result_t NumberDataField::readSymbols(SymbolString& input, bool negative = (m_dataType.flags & SIG) != 0 && (value & (1 << (m_bitCount - 1))) != 0; if (m_bitCount == 32) { if (!negative) { - if (m_divisor <= 1) + if (m_divisor < 0) + output << static_cast((float)value * (float)(-m_divisor)); + else if (m_divisor <= 1) output << static_cast(value); else output << setprecision(m_precision) @@ -938,7 +993,9 @@ result_t NumberDataField::readSymbols(SymbolString& input, else signedValue = (int) value; - if (m_divisor <= 1) { + if (m_divisor < 0) + output << static_cast((float)signedValue * (float)(-m_divisor)); + else if (m_divisor <= 1) { if ((m_dataType.flags & (FIX|BCD)) == (FIX|BCD)) output << setw(m_length * 2) << setfill('0'); output << static_cast(signedValue) << setw(0); @@ -962,7 +1019,7 @@ result_t NumberDataField::writeSymbols(istringstream& input, return RESULT_ERR_EOF; // input too short else { char* strEnd = NULL; - if (m_divisor <= 1) { + if (m_divisor >= 0 && m_divisor <= 1) { if ((m_dataType.flags & SIG) != 0) { long int signedValue = strtol(str, &strEnd, 10); if (signedValue < 0 && m_bitCount != 32) @@ -974,13 +1031,15 @@ result_t NumberDataField::writeSymbols(istringstream& input, value = (unsigned int)strtoul(str, &strEnd, 10); if (strEnd == NULL || *strEnd != 0) return RESULT_ERR_INVALID_NUM; // invalid value - } - else { + } else { char* strEnd = NULL; double dvalue = strtod(str, &strEnd); if (strEnd == NULL || *strEnd != 0) return RESULT_ERR_INVALID_NUM; // invalid value - dvalue = round(dvalue * m_divisor); + if (m_divisor < 0) + dvalue = round(dvalue / -m_divisor); + else + dvalue = round(dvalue * m_divisor); if ((m_dataType.flags & SIG) != 0) { if (dvalue < -(1LL << (8 * m_length)) || dvalue >= (1LL << (8 * m_length))) return RESULT_ERR_OUT_OF_RANGE; // value out of range @@ -1014,7 +1073,7 @@ result_t NumberDataField::writeSymbols(istringstream& input, result_t ValueListDataField::derive(string name, string comment, string unit, const PartType partType, - unsigned int divisor, map values, + int divisor, map values, vector& fields) { if (m_partType != pt_any && partType == pt_any) @@ -1166,7 +1225,7 @@ unsigned char DataFieldSet::getLength(PartType partType) result_t DataFieldSet::derive(string name, string comment, string unit, const PartType partType, - unsigned int divisor, map values, + int divisor, map values, vector& fields) { if (!values.empty()) diff --git a/src/lib/ebus/data.h b/src/lib/ebus/data.h index a8a7a282..42bd3f0b 100644 --- a/src/lib/ebus/data.h +++ b/src/lib/ebus/data.h @@ -75,14 +75,14 @@ static const unsigned int REQ = 0x100;//!< value may not be NULL /** The structure for defining field types with their properties. */ typedef struct { - const char* name; //!< field identifier - const unsigned char bitCount; //!< number of bits (maximum length if @a ADJ flag is set, must be multiple of 8 with flag @a BCD) - const BaseType type; //!< base data type - const unsigned short flags; //!< flags (e.g. @a BCD) - const unsigned int replacement; //!< replacement value (fill-up value for @a bt_str / @a bt_hexstr, no replacement if equal to @a minValueOrLength for @a bt_num) - const unsigned int minValueOrLength; //!< minimum binary value (minimum length of string for @a StringDataField) - const unsigned int maxValueOrLength; //!< maximum binary value (maximum length of string for @a StringDataField) - const unsigned short divisorOrFirstBit; //!< @a bt_number: divisor or offset to first bit (if (@a bitCount%8)!=0) + const char* name; //!< field identifier + const unsigned char bitCount; //!< number of bits (maximum length if @a ADJ flag is set, must be multiple of 8 with flag @a BCD) + const BaseType type; //!< base data type + const unsigned short flags; //!< flags (e.g. @a BCD) + const unsigned int replacement; //!< replacement value (fill-up value for @a bt_str / @a bt_hexstr, no replacement if equal to @a minValueOrLength for @a bt_num) + const unsigned int minValueOrLength; //!< minimum binary value (minimum length of string for @a StringDataField) + const unsigned int maxValueOrLength; //!< maximum binary value (maximum length of string for @a StringDataField) + const short divisorOrFirstBit; //!< @a bt_number: divisor (negative for reciprocal) or offset to first bit (if (@a bitCount%8)!=0) } dataType_t; /** the maximum position within master or slave data. */ @@ -100,6 +100,18 @@ typedef struct { */ 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 to stdout. * @param begin the iterator to the beginning of the items. @@ -171,14 +183,14 @@ public: * @param comment the field comment, or empty to use this fields comment. * @param unit the value unit, or empty to use this fields unit (if applicable). * @param partType the message part in which the field is stored. - * @param divisor the extra divisor to apply on the value, or 1 for none (if applicable). + * @param divisor the extra divisor (negative for reciprocal) to apply on the value, or 1 for none (if applicable). * @param values the value=text assignments, or empty to use this fields assignments (if applicable). * @param fields the @a vector to which created @a SingleDataField instances shall be added. * @return @a RESULT_OK on success, or an error code. */ virtual result_t derive(string name, string comment, string unit, const PartType partType, - unsigned int divisor, map values, + int divisor, map values, vector& fields) = 0; /** @@ -383,7 +395,7 @@ public: // @copydoc virtual result_t derive(string name, string comment, string unit, const PartType partType, - unsigned int divisor, map values, + int divisor, map values, vector& fields); // @copydoc @@ -480,12 +492,12 @@ public: * @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 to apply on the value, or 1 for none. + * @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 unsigned int divisor); + const int divisor); /** * Destructor. @@ -495,7 +507,7 @@ public: // @copydoc virtual result_t derive(string name, string comment, string unit, const PartType partType, - unsigned int divisor, map values, + int divisor, map values, vector& fields); // @copydoc @@ -511,8 +523,8 @@ protected: private: - /** the combined divisor to apply on the value, or 1 for none. */ - const unsigned int m_divisor; + /** 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; @@ -553,7 +565,7 @@ public: // @copydoc virtual result_t derive(string name, string comment, - string unit, const PartType partType, unsigned int divisor, + string unit, const PartType partType, int divisor, map values, vector& fields); @@ -611,7 +623,7 @@ public: // @copydoc virtual result_t derive(string name, string comment, string unit, const PartType partType, - unsigned int divisor, map values, + int divisor, map values, vector& fields); /** diff --git a/src/lib/ebus/test/test_data.cpp b/src/lib/ebus/test/test_data.cpp index f20ebdb9..f3dbca50 100644 --- a/src/lib/ebus/test/test_data.cpp +++ b/src/lib/ebus/test/test_data.cpp @@ -119,40 +119,52 @@ int main() {"x,,bcd:4","", "10feffff0100009a00", "00", "rw"}, {"x,,str:16", "0123456789ABCDEF", "10feffff1130313233343536373839414243444546", "00", ""}, {"x,,uch:17", "", "10feffff00", "00", "c"}, - {"x,s,uch", "0", "1025ffff0310111213", "0300010203", "W"}, - {"x,s,uch", "0", "1025ffff00", "0100", ""}, + {"x,s,uch", "0", "1025ffff0310111213", "0300010203", "W"}, + {"x,s,uch", "0", "1025ffff00", "0100", ""}, {"x,s,uch,,,,y,m,uch", "3;2","1025ffff0103", "0102", ""}, - {"x,,uch", "38", "10feffff0126", "00", ""}, - {"x,,uch", "0", "10feffff0100", "00", ""}, - {"x,,uch", "254", "10feffff01fe", "00", ""}, - {"x,,uch", "-", "10feffff01ff", "00", ""}, - {"x,,sch", "-90", "10feffff01a6", "00", ""}, - {"x,,sch", "0", "10feffff0100", "00", ""}, - {"x,,sch", "-1", "10feffff01ff", "00", ""}, - {"x,,sch", "-", "10feffff0180", "00", ""}, - {"x,,sch", "-127", "10feffff0181", "00", ""}, - {"x,,sch", "127", "10feffff017f", "00", ""}, - {"x,,d1b", "-90", "10feffff01a6", "00", ""}, - {"x,,d1b", "0", "10feffff0100", "00", ""}, - {"x,,d1b", "-1", "10feffff01ff", "00", ""}, - {"x,,d1b", "-", "10feffff0180", "00", ""}, - {"x,,d1b", "-127", "10feffff0181", "00", ""}, - {"x,,d1b", "127", "10feffff017f", "00", ""}, - {"x,,d1c", "19.5", "10feffff0127", "00", ""}, - {"x,,d1c", "0.0", "10feffff0100", "00", ""}, - {"x,,d1c", "100.0", "10feffff01c8", "00", ""}, - {"x,,d1c", "-", "10feffff01ff", "00", ""}, + {"x,,uch", "38", "10feffff0126", "00", ""}, + {"x,,uch", "0", "10feffff0100", "00", ""}, + {"x,,uch", "254", "10feffff01fe", "00", ""}, + {"x,,uch", "-", "10feffff01ff", "00", ""}, + {"x,,uch,10", "3.8", "10feffff0126", "00", ""}, + {"x,,uch,-10", "380","10feffff0126", "00", ""}, + {"x,,sch", "-90", "10feffff01a6", "00", ""}, + {"x,,sch", "0", "10feffff0100", "00", ""}, + {"x,,sch", "-1", "10feffff01ff", "00", ""}, + {"x,,sch", "-", "10feffff0180", "00", ""}, + {"x,,sch", "-127", "10feffff0181", "00", ""}, + {"x,,sch", "127", "10feffff017f", "00", ""}, + {"x,,sch,10", "-9.0","10feffff01a6", "00", ""}, + {"x,,sch,-10","-900","10feffff01a6", "00", ""}, + {"x,,d1b", "-90", "10feffff01a6", "00", ""}, + {"x,,d1b", "0", "10feffff0100", "00", ""}, + {"x,,d1b", "-1", "10feffff01ff", "00", ""}, + {"x,,d1b", "-", "10feffff0180", "00", ""}, + {"x,,d1b", "-127", "10feffff0181", "00", ""}, + {"x,,d1b", "127", "10feffff017f", "00", ""}, + {"x,,d1b,-10","-900","10feffff01a6", "00", ""}, + {"x,,d1c", "19.5", "10feffff0127", "00", ""}, + {"x,,d1c", "0.0", "10feffff0100", "00", ""}, + {"x,,d1c", "100.0", "10feffff01c8", "00", ""}, + {"x,,d1c", "-", "10feffff01ff", "00", ""}, {"x,,uin", "38", "10feffff022600", "00", ""}, {"x,,uin", "0", "10feffff020000", "00", ""}, {"x,,uin", "65534", "10feffff02feff", "00", ""}, {"x,,uin", "-", "10feffff02ffff", "00", ""}, {"x,,uin,10", "3.8", "10feffff022600", "00", ""}, + {"x,,uin,-10","380", "10feffff022600", "00", ""}, + {"uin10,uin,-10","", "", "", "t"}, // template + {"x,,uin10","380", "10feffff022600", "00", ""}, // template reference + {"x,,uin10,-10","3800","10feffff022600", "00", ""}, // template reference, valid divider product + {"x,,uin10,10","","", "", "c"}, // template reference, invalid divider product {"x,,sin", "-90", "10feffff02a6ff", "00", ""}, {"x,,sin", "0", "10feffff020000", "00", ""}, {"x,,sin", "-1", "10feffff02ffff", "00", ""}, {"x,,sin", "-", "10feffff020080", "00", ""}, {"x,,sin", "-32767", "10feffff020180", "00", ""}, {"x,,sin", "32767", "10feffff02ff7f", "00", ""}, + {"x,,sin,10","-9.0", "10feffff02a6ff", "00", ""}, + {"x,,sin,-10","-900","10feffff02a6ff", "00", ""}, {"x,,flt", "-0.090", "10feffff02a6ff", "00", ""}, {"x,,flt", "0.000", "10feffff020000", "00", ""}, {"x,,flt", "-0.001", "10feffff02ffff", "00", ""}, @@ -175,9 +187,13 @@ int main() {"x,,ulg", "0", "10feffff0400000000", "00", ""}, {"x,,ulg", "4294967294", "10feffff04feffffff", "00", ""}, {"x,,ulg", "-", "10feffff04ffffffff", "00", ""}, + {"x,,ulg,10","3.8", "10feffff0426000000", "00", ""}, + {"x,,ulg,-10","380", "10feffff0426000000", "00", ""}, {"x,,slg", "-90", "10feffff04a6ffffff", "00", ""}, {"x,,slg", "0", "10feffff0400000000", "00", ""}, {"x,,slg", "-1", "10feffff04ffffffff", "00", ""}, + {"x,,slg,10", "-9.0", "10feffff04a6ffffff", "00", ""}, + {"x,,slg,-10", "-900", "10feffff04a6ffffff", "00", ""}, {"x,,bi3", "1", "10feffff0108", "00", ""}, {"x,,bi3", "0", "10feffff0100", "00", ""}, {"x,,bi3,0=off;1=on","on", "10feffff0108", "00", ""}, @@ -203,6 +219,8 @@ int main() {"x,,bi6:2,,,,y,,bi0:2,,,,t,,uch", "2;1;9","10feffff03800109", "00", ""}, // bit combination {"temp,d2b,,°C,Aussentemperatur","","", "", "t"}, // template with relative pos {"x,,temp","18.004","10fe0700020112", "00", ""}, // reference to template + {"x,,temp,10","1.8004","10fe0700020112", "00", ""}, // reference to template, valid divider product + {"x,,temp,-10","","", "", "c"}, // reference to template, invalid divider product {"relrel,d2b,,,,y,d1c","","", "", "t"}, // template struct with relative pos {"x,,relrel","18.004;9.5","10fe070003011213", "00", ""}, // reference to template struct {"trelrel,temp;temp","","", "", "t"}, // template struct with relative pos and ref to templates