11using NLog ;
22
33/// <summary>
4- /// This class defines a basic telegram with data. No special parsing of the
4+ /// This class defines a basic telegram with data. No special parsing of the
55/// content is done here. Check the specialized classes for more detail
66/// </summary>
77public class BaseTelegram : IEquatable < BaseTelegram >
@@ -11,86 +11,141 @@ public class BaseTelegram : IEquatable<BaseTelegram>
1111 /// </summary>
1212 static readonly Logger log = LogManager . GetCurrentClassLogger ( ) ;
1313
14- #region Properties
14+
15+ #region Constants
1516 /// <summary>
16- /// Start Sequence of the telegram
17+ /// Maximum supported data length
1718 /// </summary>
18- public UInt16 Start { get => ( UInt16 ) ( ( Raw [ 0 ] << 8 ) + Raw [ 1 ] ) ; }
19+ private const byte MAX_DATA_LEN = 32 ;
1920
2021 /// <summary>
21- /// Source of the telegram
22+ /// Minimum length of a telegram. This contains the following data:
23+ /// - Type (2 bytes)
24+ /// - Destination (1 byte)
25+ /// - Source (1 byte)
26+ /// - Data length (1 byte)
27+ /// - Checksum (1 byte)
28+ /// - End byte (1 byte)
2229 /// </summary>
23- public byte Source { get => Raw [ POS_SRC ] ; }
30+ private const byte MIN_DATA_LEN = 7 ;
2431
2532 /// <summary>
26- /// Destination of the telegram
33+ /// End byte of the telegram
2734 /// </summary>
28- public byte Destination { get => Raw [ POS_DES ] ; }
35+ private const byte END_TELEGRAM = 0x0D ;
2936
3037 /// <summary>
31- /// User Data
38+ /// Offset of the high Byte of the the telegram type
3239 /// </summary>
33- public byte [ ] PDU { get ; }
40+ private const byte POS_TYPE_H = 0 ;
41+ /// <summary>
42+ /// Offset of the low Byte of the the telegram type
43+ /// </summary>
44+ private const byte POS_TYPE_L = 1 ;
45+ /// <summary>
46+ /// Position of the destination id in the raw data
47+ /// </summary>
48+ private const byte POS_DES = 2 ;
49+ /// <summary>
50+ /// Position of the source id in the raw data
51+ /// </summary>
52+ private const byte POS_SRC = 3 ;
53+ /// <summary>
54+ /// Position of the data length in the raw data
55+ /// </summary>
56+ private const byte POS_LEN = 4 ;
57+ #endregion
3458
59+ #region Properties
3560 /// <summary>
36- /// Received checksum
61+ /// Type of the telegram as raw value
3762 /// </summary>
38- public byte Checksum { get => Raw [ POS_LEN + PDU . Length + 1 ] ; }
63+ public UInt16 RawType
64+ {
65+ get => ( UInt16 ) ( ( Raw [ POS_TYPE_H ] << 8 ) + Raw [ POS_TYPE_L ] ) ;
66+ }
3967
4068 /// <summary>
41- /// Copy of the received raw data of the telegram
69+ /// Destination / recipient of the telegram
4270 /// </summary>
43- public byte [ ] Raw { get ; }
71+ public byte Destination { get => Raw [ POS_DES ] ; }
4472
4573 /// <summary>
46- /// Are the raw data valid against the Checksum
74+ /// Source / sender of the telegram
4775 /// </summary>
48- public bool Valid { get ; }
76+ public byte Source { get => Raw [ POS_SRC ] ; }
4977
50- public enum TelegramType
78+ /// <summary>
79+ /// Identifier for the telegram type. Is is a mixture of the telegram type,
80+ /// the destination and the source.
81+ /// </summary>
82+ public UInt16 Id
5183 {
52- READ_REQUEST = 0xC55C ,
53- READ_RESPONSE = 0xB66B
84+ get => ( UInt16 ) ( Destination << 8 | Source ) ;
5485 }
86+
5587 /// <summary>
56- /// Type of the telegram
88+ /// User Data / specific to the telegram
5789 /// </summary>
58- public TelegramType Type { get => ( TelegramType ) Start ; }
59-
60- #endregion
90+ public byte [ ] PDU { get ; }
6191
62- #region Constants
6392 /// <summary>
64- /// Maximum supported data length
93+ /// Received checksum
6594 /// </summary>
66- private const byte MAX_DATA_LEN = 32 ;
95+ public byte Checksum { get => Raw [ POS_LEN + PDU . Length + 1 ] ; }
6796
6897 /// <summary>
69- /// End byte of the telegram
98+ /// Complete telegram as raw data
7099 /// </summary>
71- private const byte END_TELEGRAM = 0x0D ;
100+ public byte [ ] Raw { get ; }
72101
73102 /// <summary>
74- /// Position of the source id in the raw data
103+ /// The raw PDU is valid against the received checksum
75104 /// </summary>
76- private const byte POS_SRC = 2 ;
105+ public bool Valid
106+ {
107+ get
108+ {
109+ byte calcCheck = Raw [ POS_LEN ] ;
110+ foreach ( byte b in PDU )
111+ {
112+ calcCheck ^= b ;
113+ }
114+
115+ log . Trace ( $ "Checksum: read={ Checksum . ToString ( "X2" ) } , calc={ calcCheck . ToString ( "X2" ) } ") ;
116+ return calcCheck == Checksum ;
117+ }
118+ }
119+
77120 /// <summary>
78- /// Position of the destination id in the raw data
121+ /// Possible types of the telegram
79122 /// </summary>
80- private const byte POS_DES = 3 ;
123+ public enum TelegramType
124+ {
125+ /// <summary>
126+ /// Request telegram triggered by bus master to the unit
127+ /// </summary>
128+ REQUEST = 0xC55C ,
129+ /// <summary>
130+ /// Telegram sent as a response to a request from the unit to the bus master
131+ /// </summary>
132+ RESPONSE = 0xB66B
133+ }
134+
81135 /// <summary>
82- /// Position of the data length in the raw data
136+ /// Type of the telegram
83137 /// </summary>
84- private const byte POS_LEN = 4 ;
138+ public TelegramType Type { get => ( TelegramType ) RawType ; }
139+
85140 #endregion
86141
87142 /// <summary>
88- /// Internal constructor for specialized classes
143+ /// Internal constructor for specialized classes. Create empty arrays
89144 /// </summary>
90145 protected BaseTelegram ( )
91146 {
92- PDU = new byte [ 1 ] ;
93- Raw = new byte [ 1 ] ;
147+ PDU = Array . Empty < byte > ( ) ;
148+ Raw = Array . Empty < byte > ( ) ;
94149 }
95150
96151 /// <summary>
@@ -104,56 +159,52 @@ protected BaseTelegram(BaseTelegram c)
104159
105160 this . PDU = new byte [ c . PDU . Length ] ;
106161 Array . Copy ( c . PDU , this . PDU , this . PDU . Length ) ;
107-
108- this . Valid = c . Valid ;
109162 }
110163
111164 /// <summary>
112165 /// Create a new base telegram based on the given raw data
113166 /// </summary>
114167 /// <param name="rawData">raw data of one telegram</param>
115- /// <exception cref="ArgumentException">Given data length in the raw data is invalid.</exception>
168+ /// <exception cref="ArgumentNullException">Raw data is null.</exception>
169+ /// <exception cref="ArgumentException">Raw data is too short.</exception>
170+ /// <exception cref="ArgumentException">Invalid data length in the raw data.</exception>
171+ /// <exception cref="ArgumentException">Raw data does not contain End tag.</exception>
116172 public BaseTelegram ( byte [ ] rawData )
117173 {
174+ // Basic validation
175+ ArgumentNullException . ThrowIfNull ( rawData ) ;
176+ if ( rawData . Length < MIN_DATA_LEN )
177+ {
178+ throw new ArgumentException ( "Raw data is too short" ) ;
179+ }
180+
118181 // Copy raw data
119182 Raw = new byte [ rawData . Length ] ;
120183 Array . Copy ( rawData , Raw , Raw . Length ) ;
121184
122185 // fetch user data
123- byte dataLen = rawData [ POS_LEN ] ;
124- if ( dataLen > MAX_DATA_LEN )
186+ byte pduLen = rawData [ POS_LEN ] ;
187+ if ( pduLen > MAX_DATA_LEN )
125188 {
126- throw new ArgumentException ( $ "Invalid data len { dataLen } . Max supported: { MAX_DATA_LEN } ") ;
189+ throw new ArgumentException ( $ "Invalid data len { pduLen } . Max supported: { MAX_DATA_LEN } ") ;
127190 }
128- PDU = new byte [ dataLen ] ;
191+
192+ // copy user data to PDU array
193+ PDU = new byte [ pduLen ] ;
129194 Array . Copy ( rawData , POS_LEN + 1 , PDU , 0 , PDU . Length ) ;
130195
131- /* Verify checksum
132- * Checksum is calculated as XOR of the user data, including the length byte
133- */
134- byte calcCheck = dataLen ;
135- foreach ( byte b in PDU )
136- {
137- calcCheck ^= b ;
138- }
139- log . Trace ( $ "Checksum: read={ Checksum . ToString ( "X2" ) } , calc={ calcCheck . ToString ( "X2" ) } ") ;
140- Valid = ( calcCheck == Checksum ) ;
196+ // Verify checksum
141197 if ( Valid == false )
142198 {
143- log . Warn ( $ "Telegram has an invalid checksum read: { Checksum . ToString ( "X2" ) } , calc: { calcCheck . ToString ( "X2" ) } ") ;
199+ log . Warn ( "Telegram has an invalid checksum" ) ;
144200 }
145201
146- // Check end telegram
147- try
148- {
149- if ( rawData . Length < ( 5 + 1 + dataLen ) || rawData [ 5 + 1 + dataLen ] != END_TELEGRAM )
150- {
151- log . Warn ( "RawData does not hold Endtag" ) ;
152- }
153- }
154- catch ( IndexOutOfRangeException )
202+ // Check end telegram value
203+ int pos_end_tag = MIN_DATA_LEN - 1 + pduLen ;
204+ if ( rawData . Length < pos_end_tag || rawData [ pos_end_tag ] != END_TELEGRAM )
155205 {
156- log . Warn ( "Rawdata does not contain End tag" ) ;
206+ log . Error ( "RawData does not hold Endtag" ) ;
207+ throw new ArgumentException ( "Raw data does not contain End tag" ) ;
157208 }
158209 }
159210
@@ -192,8 +243,14 @@ public virtual string ToStringDetailed()
192243 return ToString ( ) ;
193244 }
194245
246+ /// <summary>
247+ /// Compare two telegrams based on the raw data
248+ /// </summary>
249+ /// <param name="other">Second object to compare</param>
250+ /// <returns></returns>
195251 public bool Equals ( BaseTelegram ? other )
196252 {
197253 return Array . Equals ( this . Raw , other ? . Raw ) ;
198254 }
199- }
255+
256+ }
0 commit comments