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