1 /* 2 * ebusd - daemon for communication with eBUS heating systems. 3 * Copyright (C) 2014-2021 John Baier <ebusd@ebusd.eu> 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #ifndef LIB_EBUS_DATATYPE_H_ 20 #define LIB_EBUS_DATATYPE_H_ 21 22 #include <stdint.h> 23 #include <string> 24 #include <iostream> 25 #include <sstream> 26 #include <fstream> 27 #include <vector> 28 #include <list> 29 #include <map> 30 #include "lib/ebus/symbol.h" 31 #include "lib/ebus/result.h" 32 #include "lib/ebus/filereader.h" 33 34 namespace ebusd { 35 36 /** @file lib/ebus/datatype.h 37 * Classes, functions, and constants related to decoding/encoding of symbols 38 * on the eBUS to/from readable values and registry of data types. 39 * 40 * A @a DataType is one of @a StringDataType, @a DateTimeDataType, 41 * @a NumberDataType, or @a BitsDataType. 42 * 43 * The particular eBUS specification types like e.g. @a D1C are defined by 44 * using one of these base data types with certain flags, such as #BCD, #FIX, 45 * #REQ, see @a DataType. 46 * 47 * Each @a DataType can be converted from a @a SymbolString to an 48 * @a ostringstream (see @a DataType#readSymbols() methods) or vice versa from 49 * an @a istringstream to a @a SymbolString (see @a DataType#writeSymbols()). 50 */ 51 52 using std::map; 53 using std::list; 54 using std::istringstream; 55 using std::ostringstream; 56 57 /** the separator character used between base type name and length (in CSV only). */ 58 #define LENGTH_SEPARATOR ':' 59 60 /** the replacement string for undefined values (in UI and CSV). */ 61 #define NULL_VALUE "-" 62 63 /** the separator character used between fields (in UI only). */ 64 #define UI_FIELD_SEPARATOR ';' 65 66 /** the maximum allowed position within master or slave data. */ 67 #define MAX_POS 24 68 69 /** the maximum allowed field length. */ 70 #define MAX_LEN 31 71 72 /** the field length indicating remainder of input. */ 73 #define REMAIN_LEN 255 74 75 /** the maximum divisor value. */ 76 #define MAX_DIVISOR 1000000000 77 78 /** the maximum value for value lists. */ 79 #define MAX_VALUE (0xFFFFFFFFu) 80 81 typedef unsigned int OutputFormatBaseType; 82 83 enum OutputFormat : OutputFormatBaseType { 84 /** no bit set at all. */ 85 OF_NONE = 0, 86 87 /** bit flag for @a OutputFormat: include names. */ 88 OF_NAMES = 1 << 0, 89 90 /** bit flag for @a OutputFormat: include units. */ 91 OF_UNITS = 1 << 1, 92 93 /** bit flag for @a OutputFormat: include comments. */ 94 OF_COMMENTS = 1 << 2, 95 96 /** bit flag for @a OutputFormat: numeric format (keep numeric value of value=name pairs). */ 97 OF_NUMERIC = 1 << 3, 98 99 /** bit flag for @a OutputFormat: value=name format for such pairs. */ 100 OF_VALUENAME = 1 << 4, 101 102 /** bit flag for @a OutputFormat: JSON format. */ 103 OF_JSON = 1 << 5, 104 105 /** bit flag for @a OutputFormat: short format (only name and value for fields). */ 106 OF_SHORT = 1 << 6, 107 108 /** bit flag for @a OutputFormat: include all attributes. */ 109 OF_ALL_ATTRS = 1 << 7, 110 111 /** bit flag for @a OutputFormat: include message/field definition. */ 112 OF_DEFINITION = 1 << 8, 113 }; 114 115 constexpr inline enum OutputFormat operator| (enum OutputFormat self, enum OutputFormat other) { 116 return (enum OutputFormat)((OutputFormatBaseType)self | (OutputFormatBaseType)other); 117 } 118 119 inline enum OutputFormat& operator|= (enum OutputFormat& self, enum OutputFormat other) { 120 self = self | other; 121 return self; 122 } 123 124 constexpr inline enum OutputFormat operator& (enum OutputFormat self, enum OutputFormat other) { 125 return (enum OutputFormat)((OutputFormatBaseType)self & (OutputFormatBaseType)other); 126 } 127 128 inline enum OutputFormat& operator&= (enum OutputFormat& self, enum OutputFormat other) { 129 self = self & other; 130 return self; 131 } 132 133 constexpr inline enum OutputFormat operator~ (enum OutputFormat self) { 134 return (enum OutputFormat)(~(OutputFormatBaseType)self); 135 } 136 137 /** the message part in which a data field is stored. */ 138 enum PartType { 139 pt_any, //!< stored in any data (master or slave) 140 pt_masterData, //!< stored in master data 141 pt_slaveData, //!< stored in slave data 142 }; 143 144 /** bit flag for @a DataType: adjustable length, bitCount is maximum length. */ 145 #define ADJ 0x01 146 147 /** bit flag for @a DataType: binary representation is BCD. */ 148 #define BCD 0x02 149 150 /** bit flag for @a DataType: reverted binary representation (most significant byte first). */ 151 #define REV 0x04 152 153 /** bit flag for @a DataType: signed value. */ 154 #define SIG 0x08 155 156 /** bit flag for @a DataType: ignore value during read and write. */ 157 #define IGN 0x10 158 159 /** bit flag for @a DataType: fixed width formatting. */ 160 #define FIX 0x20 161 162 /** bit flag for @a DataType: value may not be NULL. */ 163 #define REQ 0x40 164 165 /** bit flag for @a DataType: binary representation is hex converted to decimal and interpreted as 2 digits 166 * (also requires #BCD). */ 167 #define HCD 0x80 168 169 /** bit flag for @a DataType: exponential numeric representation. */ 170 #define EXP 0x100 171 172 /** bit flag for @a DataType: forced value list defaulting to week days. */ 173 #define DAY 0x200 174 175 /** bit flag for @a DataType: numeric type with base class @a NumberDataType. */ 176 #define NUM 0x400 177 178 /** bit flag for @a DataType: special marker for certain types. */ 179 #define SPE 0x800 180 181 /** bit flag for @a DataType: stored duplicate for backwards compatibility, not to be traversed in lists any more. */ 182 #define DUP 0x1000 183 184 /** 185 * Base class for all kinds of data types. 186 */ 187 class DataType { 188 public: 189 /** 190 * Constructs a new instance. 191 * @param id the type identifier. 192 * @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). 193 * @param flags the combination of flags (like #BCD). 194 * @param replacement the replacement value (fill-up value for @a StringDataType, no replacement if equal to 195 * @a NumberDataType#minValue). 196 */ DataType(const string & id,size_t bitCount,uint16_t flags,unsigned int replacement)197 DataType(const string& id, size_t bitCount, uint16_t flags, unsigned int replacement) 198 : m_id(id), m_bitCount(bitCount), m_flags(flags), m_replacement(replacement) {} 199 200 /** 201 * Destructor. 202 */ ~DataType()203 virtual ~DataType() { } 204 205 /** 206 * @return the type identifier. 207 */ getId()208 string getId() const { return m_id; } 209 210 /** 211 * @return the number of bits (maximum length if #ADJ flag is set). 212 */ getBitCount()213 size_t getBitCount() const { return m_bitCount; } 214 215 /** 216 * Check whether a flag is set. 217 * @param flag the flag to check (like #BCD). 218 * @return whether the flag is set. 219 */ hasFlag(unsigned int flag)220 bool hasFlag(unsigned int flag) const { return (m_flags & flag) != 0; } 221 222 /** 223 * @return whether this type is ignored. 224 */ isIgnored()225 bool isIgnored() const { return hasFlag(IGN); } 226 227 /** 228 * @return whether this type has an adjustable length. 229 */ isAdjustableLength()230 bool isAdjustableLength() const { return hasFlag(ADJ); } 231 232 /** 233 * @return whether this field is derived from @a NumberDataType. 234 */ isNumeric()235 bool isNumeric() const { return hasFlag(NUM); } 236 237 /** 238 * @return the replacement value (fill-up value for @a StringDataType, no replacement if equal to 239 * @a NumberDataType#minValue). 240 */ getReplacement()241 unsigned int getReplacement() const { return m_replacement; } 242 243 /** 244 * Dump the type identifier with the specified length and optionally the 245 * divisor to the output. 246 * @param outputFormat the @a OutputFormat options. 247 * @param length the number of symbols to read/write. 248 * @param appendDivisor whether to append the divisor (if available). 249 * @param output the @a ostream to dump to. 250 * @return true when a non-default divisor was written to the output. 251 */ 252 virtual bool dump(OutputFormat outputFormat, size_t length, bool appendDivisor, ostream* output) const; 253 254 /** 255 * Internal method for reading the numeric raw value from a @a SymbolString. 256 * @param offset the offset in the @a SymbolString. 257 * @param length the number of symbols to read. 258 * @param input the @a SymbolString to read the binary value from. 259 * @param value the variable in which to store the numeric raw value. 260 * @return @a RESULT_OK on success, or an error code. 261 */ 262 virtual result_t readRawValue(size_t offset, size_t length, const SymbolString& input, 263 unsigned int* value) const = 0; 264 265 /** 266 * Internal method for reading the field from a @a SymbolString. 267 * @param offset the offset in the data of the @a SymbolString. 268 * @param length the number of symbols to read. 269 * @param input the @a SymbolString to read the binary value from. 270 * @param outputFormat the @a OutputFormat options to use. 271 * @param output the ostream to append the formatted value to. 272 * @return @a RESULT_OK on success, or an error code. 273 */ 274 virtual result_t readSymbols(size_t offset, size_t length, const SymbolString& input, 275 OutputFormat outputFormat, ostream* output) const = 0; 276 277 /** 278 * Internal method for writing the field to a @a SymbolString. 279 * @param offset the offset in the @a SymbolString. 280 * @param length the number of symbols to write, or @a REMAIN_LEN. 281 * @param input the @a istringstream to parse the formatted value from. 282 * @param output the @a SymbolString to write the binary value to. 283 * @param usedLength the variable in which to store the used length in bytes, or nullptr. 284 * @return @a RESULT_OK on success, or an error code. 285 */ 286 virtual result_t writeSymbols(size_t offset, size_t length, istringstream* input, 287 SymbolString* output, size_t* usedLength) const = 0; 288 289 290 protected: 291 /** the type identifier. */ 292 const string m_id; 293 294 /** the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). */ 295 const size_t m_bitCount; 296 297 /** the combination of flags (like #BCD). */ 298 const uint16_t m_flags; 299 300 /** the replacement value (fill-up value for @a StringDataType, no replacement if equal to 301 * @a NumberDataType#m_minValue). */ 302 const unsigned int m_replacement; 303 }; 304 305 306 /** 307 * A string based @a DataType. 308 */ 309 class StringDataType : public DataType { 310 public: 311 /** 312 * Constructs a new instance. 313 * @param id the type identifier. 314 * @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). 315 * @param flags the combination of flags (like #BCD). 316 * @param replacement the replacement value (fill-up value). 317 * @param isHex true for hex digits instead of characters. 318 */ 319 StringDataType(const string& id, size_t bitCount, uint16_t flags, 320 unsigned int replacement, bool isHex = false) DataType(id,bitCount,flags,replacement)321 : DataType(id, bitCount, flags, replacement), m_isHex(isHex) {} 322 323 /** 324 * Destructor. 325 */ ~StringDataType()326 virtual ~StringDataType() {} 327 328 // @copydoc 329 bool dump(OutputFormat outputFormat, size_t length, bool appendDivisor, ostream* output) const override; 330 331 // @copydoc 332 result_t readRawValue(size_t offset, size_t length, const SymbolString& input, 333 unsigned int* value) const override; 334 335 // @copydoc 336 result_t readSymbols(size_t offset, size_t length, const SymbolString& input, 337 OutputFormat outputFormat, ostream* output) const override; 338 339 // @copydoc 340 result_t writeSymbols(size_t offset, size_t length, istringstream* input, 341 SymbolString* output, size_t* usedLength) const override; 342 343 344 private: 345 /** true for hex digits instead of characters. */ 346 const bool m_isHex; 347 }; 348 349 350 /** 351 * A date/time based @a DataType. 352 */ 353 class DateTimeDataType : public DataType { 354 public: 355 /** 356 * Constructs a new instance. 357 * @param id the type identifier. 358 * @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). 359 * @param flags the combination of flags (like #BCD). 360 * @param replacement the replacement value. 361 * @param hasDate true if date part is present. 362 * @param hasTime true if time part is present. 363 * @param resolution the the resolution in minutes for time types, or 1. 364 */ DateTimeDataType(const string & id,size_t bitCount,uint16_t flags,unsigned int replacement,bool hasDate,bool hasTime,int16_t resolution)365 DateTimeDataType(const string& id, size_t bitCount, uint16_t flags, unsigned int replacement, 366 bool hasDate, bool hasTime, int16_t resolution) 367 : DataType(id, bitCount, flags, replacement), m_hasDate(hasDate), m_hasTime(hasTime), 368 m_resolution(resolution == 0 ? 1 : resolution) {} 369 370 /** 371 * Destructor. 372 */ ~DateTimeDataType()373 virtual ~DateTimeDataType() {} 374 375 // @copydoc 376 bool dump(OutputFormat outputFormat, size_t length, bool appendDivisor, ostream* output) const override; 377 378 /** 379 * @return true if date part is present. 380 */ hasDate()381 bool hasDate() const { return m_hasDate; } 382 383 /** 384 * @return true if time part is present. 385 */ hasTime()386 bool hasTime() const { return m_hasTime; } 387 388 /** 389 * @return the resolution in minutes for time types, or 1. 390 */ getResolution()391 int16_t getResolution() const { return m_resolution; } 392 393 // @copydoc 394 result_t readRawValue(size_t offset, size_t length, const SymbolString& input, 395 unsigned int* value) const override; 396 397 // @copydoc 398 result_t readSymbols(size_t offset, size_t length, const SymbolString& input, 399 OutputFormat outputFormat, ostream* output) const override; 400 401 // @copydoc 402 result_t writeSymbols(const size_t offset, size_t length, istringstream* input, 403 SymbolString* output, size_t* usedLength) const override; 404 405 406 private: 407 /** true if date part is present. */ 408 const bool m_hasDate; 409 410 /** true if time part is present. */ 411 const bool m_hasTime; 412 413 /** the resolution in minutes for time types, or 1. */ 414 const int16_t m_resolution; 415 }; 416 417 418 /** 419 * A number based @a DataType. 420 */ 421 class NumberDataType : public DataType { 422 public: 423 /** 424 * Constructs a new instance for multiple of 8 bits. 425 * @param id the type identifier. 426 * @param bitCount the number of bits (maximum length if #ADJ flag is set). 427 * @param flags the combination of flags (like #BCD). 428 * @param replacement the replacement value (no replacement if equal to minValue). 429 * @param minValue the minimum raw value. 430 * @param maxValue the maximum raw value. 431 * @param divisor the divisor (negative for reciprocal). 432 * @param baseType the base @a NumberDataType for derived instances, or nullptr. 433 */ 434 NumberDataType(const string& id, size_t bitCount, uint16_t flags, unsigned int replacement, 435 unsigned int minValue, unsigned int maxValue, int divisor, 436 const NumberDataType* baseType = nullptr) 437 : DataType(id, bitCount, flags|NUM, replacement), m_minValue(minValue), m_maxValue(maxValue), m_divisor(divisor==0 ? 1 : divisor), 438 m_precision(calcPrecision(divisor)), m_firstBit(0), m_baseType(baseType) {} 439 440 /** 441 * Constructs a new instance for less than 8 bits. 442 * @param id the type identifier. 443 * @param bitCount the number of bits (maximum length if #ADJ flag is set). 444 * @param flags the combination of flags (like #ADJ, may not include flag #BCD). 445 * @param replacement the replacement value (no replacement if zero). 446 * @param firstBit the offset to the first bit. 447 * @param divisor the divisor (negative for reciprocal). 448 * @param baseType the base @a NumberDataType for derived instances, or nullptr. 449 */ 450 NumberDataType(const string& id, size_t bitCount, uint16_t flags, unsigned int replacement, 451 int16_t firstBit, int divisor, const NumberDataType* baseType = nullptr) 452 : DataType(id, bitCount, flags|NUM, replacement), m_minValue(0), m_maxValue((1 << bitCount)-1), m_divisor(divisor==0 ? 1 : divisor), 453 m_precision(0), m_firstBit(firstBit), m_baseType(baseType) {} 454 455 /** 456 * Destructor. 457 */ ~NumberDataType()458 virtual ~NumberDataType() {} 459 460 /** 461 * Calculate the precision from the divisor. 462 * 463 * @param divisor the divisor (negative for reciprocal). 464 * @return the precision for formatting the value. 465 */ 466 static size_t calcPrecision(int divisor); 467 468 // @copydoc 469 bool dump(OutputFormat outputFormat, size_t length, bool appendDivisor, ostream* output) const override; 470 471 /** 472 * Derive a new @a NumberDataType from this. 473 * @param divisor the extra divisor (negative for reciprocal) to apply, or 474 * 1 for none (if applicable), or 0 to keep the current value. 475 * @param bitCount the number of bits (maximum length if #ADJ flag is set, 476 * must be multiple of 8 with flag #BCD), or 0 to keep the current value. 477 * @param derived the derived @a NumberDataType, or this if derivation is 478 * not necessary. 479 * @return @a RESULT_OK on success, or an error code. 480 */ 481 virtual result_t derive(int divisor, size_t bitCount, const NumberDataType** derived) const; 482 483 /** 484 * @return the minimum raw value. 485 */ getMinValue()486 unsigned int getMinValue() const { return m_minValue; } 487 488 /** 489 * @return the maximum raw value. 490 */ getMaxValue()491 unsigned int getMaxValue() const { return m_maxValue; } 492 493 /** 494 * @return the divisor (negative for reciprocal). 495 */ getDivisor()496 int getDivisor() const { return m_divisor; } 497 498 /** 499 * @return the precision for formatting the value. 500 */ getPrecision()501 size_t getPrecision() const { return m_precision; } 502 503 /** 504 * @return the offset to the first bit. 505 */ getFirstBit()506 int16_t getFirstBit() const { return m_firstBit; } 507 508 // @copydoc 509 result_t readRawValue(size_t offset, size_t length, const SymbolString& input, 510 unsigned int* value) const override; 511 512 // @copydoc 513 result_t readSymbols(size_t offset, size_t length, const SymbolString& input, 514 const OutputFormat outputFormat, ostream* output) const override; 515 516 /** 517 * Internal method for writing the numeric raw value to a @a SymbolString. 518 * @param value the numeric raw value to write. 519 * @param offset the offset in the @a SymbolString. 520 * @param length the number of symbols to write, or @a REMAIN_LEN. 521 * @param output the @a SymbolString to write the binary value to. 522 * @param usedLength the variable in which to store the used length in bytes, 523 * or nullptr. 524 * @return @a RESULT_OK on success, or an error code. 525 */ 526 result_t writeRawValue(unsigned int value, size_t offset, size_t length, 527 SymbolString* output, size_t* usedLength) const; 528 529 // @copydoc 530 result_t writeSymbols(size_t offset, size_t length, istringstream* input, 531 SymbolString* output, size_t* usedLength) const override; 532 533 534 private: 535 /** the minimum raw value. */ 536 const unsigned int m_minValue; 537 538 /** the maximum raw value. */ 539 const unsigned int m_maxValue; 540 541 /** the divisor (negative for reciprocal). */ 542 const int m_divisor; 543 544 /** the precision for formatting the value. */ 545 const size_t m_precision; 546 547 /** the offset to the first bit. */ 548 const int16_t m_firstBit; 549 550 /** the base @a NumberDataType for derived instances. */ 551 const NumberDataType* m_baseType; 552 }; 553 554 555 /** 556 * A map of base @a DataType instances. 557 */ 558 class DataTypeList { 559 public: 560 /** 561 * Constructs a new instance and registers the known base data types. 562 */ 563 DataTypeList(); 564 565 /** 566 * Destructor. 567 */ ~DataTypeList()568 virtual ~DataTypeList() { 569 clear(); 570 } 571 572 /** 573 * Returns the singleton instance. 574 * @return the singleton @a DataTypeList instance. 575 */ 576 static DataTypeList* getInstance(); 577 578 /** 579 * Dump the type list optionally including the divisor to the output. 580 * @param outputFormat the @a OutputFormat options. 581 * @param appendDivisor whether to append the divisor (if available). 582 * @param output the @a ostream to dump to. 583 */ 584 void dump(OutputFormat outputFormat, bool appendDivisor, ostream* output) const; 585 586 /** 587 * Removes all @a DataType instances. 588 */ 589 void clear(); 590 591 /** 592 * Adds a @a DataType instance to this map. 593 * @param dataType the @a DataType instance to add. 594 * @return @a RESULT_OK on success, or an error code. 595 * Note: the caller may not free the added instance on success. 596 */ 597 result_t add(const DataType* dataType); 598 599 /** 600 * Adds a @a DataType instance for later cleanup. 601 * @param dataType the @a DataType instance to add. 602 */ addCleanup(const DataType * dataType)603 void addCleanup(const DataType* dataType) { m_cleanupTypes.push_back(dataType); } 604 605 /** 606 * Gets the @a DataType instance with the specified ID. 607 * @param id the ID string (excluding optional length suffix). 608 * @param length the length in bytes, or 0 for default. 609 * @return the @a DataType instance, or nullptr if not available. 610 * Note: the caller may not free the instance. 611 */ 612 const DataType* get(const string& id, size_t length = 0) const; 613 614 /** 615 * Returns an iterator pointing to the first ID/@a DataType pair. 616 * @return an iterator pointing to the first ID/@a DataType pair. 617 */ begin()618 map<string, const DataType*>::const_iterator begin() const { return m_typesById.begin(); } 619 620 /** 621 * Returns an iterator pointing one past the last ID/@a DataType pair. 622 * @return an iterator pointing one past the last ID/@a DataType pair. 623 */ end()624 map<string, const DataType*>::const_iterator end() const { return m_typesById.end(); } 625 626 private: 627 /** the known @a DataType instances by ID (e.g. "ID:BITS" or just "ID"). 628 * Note: adjustable length types are stored by ID only. */ 629 map<string, const DataType*> m_typesById; 630 631 /** the @a DataType instances to cleanup. */ 632 list<const DataType*> m_cleanupTypes; 633 634 /** the singleton instance. */ 635 static DataTypeList s_instance; 636 637 #ifdef HAVE_CONTRIB 638 /** true when contributed datatypes were successfully initialized. */ 639 static bool s_contrib_initialized; 640 #endif 641 }; 642 643 } // namespace ebusd 644 645 #endif // LIB_EBUS_DATATYPE_H_ 646