55#include " GridProfileParser.h"
66#include " ../Hoymiles.h"
77#include < cstring>
8+ #include < frozen/map.h>
9+ #include < frozen/string.h>
810
911const std::array<const ProfileType_t, PROFILE_TYPE_COUNT> GridProfileParser::_profileTypes = { {
1012 { 0x02 , 0x00 , " no data (yet)" },
@@ -16,6 +18,263 @@ const std::array<const ProfileType_t, PROFILE_TYPE_COUNT> GridProfileParser::_pr
1618 { 0x37 , 0x00 , " Swiss - CH_NA EEA-NE7-CH2020" },
1719} };
1820
21+ constexpr frozen::map<uint8_t , frozen::string, 12 > profile_section = {
22+ { 0x00 , " Voltage (H/LVRT)" },
23+ { 0x10 , " Frequency (H/LFRT)" },
24+ { 0x20 , " Island Detection (ID)" },
25+ { 0x30 , " Reconnection (RT)" },
26+ { 0x40 , " Ramp Rates (RR)" },
27+ { 0x50 , " Frequency Watt (FW)" },
28+ { 0x60 , " Volt Watt (VW)" },
29+ { 0x70 , " Active Power Control (APC)" },
30+ { 0x80 , " Volt Var (VV)" },
31+ { 0x90 , " Specified Power Factor (SPF)" },
32+ { 0xA0 , " Reactive Power Control (RPC)" },
33+ { 0xB0 , " Watt Power Factor (WPF)" },
34+ };
35+
36+ struct GridProfilePartialValue_t {
37+ frozen::string Name;
38+ frozen::string Unit;
39+ uint8_t Dividor;
40+ };
41+
42+ constexpr GridProfilePartialValue_t make_value (frozen::string Name, frozen::string Unit, uint8_t divisor)
43+ {
44+ GridProfilePartialValue_t v = { Name, Unit, divisor };
45+ return v;
46+ }
47+
48+ constexpr frozen::map<uint8_t , GridProfilePartialValue_t, 0x38 > value_names = {
49+ { 0x01 , make_value (" Nominale Voltage (NV)" , " V" , 10 ) },
50+ { 0x02 , make_value (" Low Voltage 1 (LV1)" , " V" , 10 ) },
51+ { 0x03 , make_value (" LV1 Maximum Trip Time (MTT)" , " s" , 10 ) },
52+ { 0x04 , make_value (" High Voltage 1 (HV1)" , " V" , 10 ) },
53+ { 0x05 , make_value (" HV1 Maximum Trip Time (MTT)" , " s" , 10 ) },
54+ { 0x06 , make_value (" Low Voltage 2 (LV2)" , " V" , 10 ) },
55+ { 0x07 , make_value (" LV2 Maximum Trip Time (MTT)" , " s" , 10 ) },
56+ { 0x08 , make_value (" High Voltage 2 (HV2)" , " V" , 10 ) },
57+ { 0x09 , make_value (" HV2 Maximum Trip Time (MTT)" , " s" , 10 ) },
58+ { 0x0a , make_value (" 10mins Average High Voltage (AHV)" , " V" , 10 ) },
59+ { 0x0b , make_value (" High Voltage 3 (HV3)" , " V" , 10 ) },
60+ { 0x0c , make_value (" HV3 Maximum Trip Time (MTT)" , " s" , 10 ) },
61+ { 0x0d , make_value (" Nominal Frequency" , " Hz" , 100 ) },
62+ { 0x0e , make_value (" Low Frequency 1 (LF1)" , " Hz" , 100 ) },
63+ { 0x0f , make_value (" LF1 Maximum Trip Time (MTT)" , " s" , 10 ) },
64+ { 0x10 , make_value (" High Frequency 1 (HF1)" , " Hz" , 100 ) },
65+ { 0x11 , make_value (" HF1 Maximum Trip time (MTT)" , " s" , 10 ) },
66+ { 0x12 , make_value (" Low Frequency 2 (LF2)" , " Hz" , 100 ) },
67+ { 0x13 , make_value (" LF2 Maximum Trip Time (MTT)" , " s" , 10 ) },
68+ { 0x14 , make_value (" High Frequency 2 (HF2)" , " Hz" , 100 ) },
69+ { 0x15 , make_value (" HF2 Maximum Trip time (MTT)" , " s" , 10 ) },
70+ { 0x16 , make_value (" ID Function Activated" , " bool" , 1 ) },
71+ { 0x17 , make_value (" Reconnect Time (RT)" , " s" , 10 ) },
72+ { 0x18 , make_value (" Reconnect High Voltage (RHV)" , " V" , 10 ) },
73+ { 0x19 , make_value (" Reconnect Low Voltage (RLV)" , " V" , 10 ) },
74+ { 0x1a , make_value (" Reconnect High Frequency (RHF)" , " Hz" , 100 ) },
75+ { 0x1b , make_value (" Reconnect Low Frequency (RLF)" , " Hz" , 100 ) },
76+ { 0x1c , make_value (" Normal Ramp up Rate(RUR_NM)" , " Rated%/s" , 100 ) },
77+ { 0x1d , make_value (" Soft Start Ramp up Rate (RUR_SS)" , " Rated%/s" , 100 ) },
78+ { 0x1e , make_value (" FW Function Activated" , " bool" , 1 ) },
79+ { 0x1f , make_value (" Start of Frequency Watt Droop (Fstart)" , " Hz" , 100 ) },
80+ { 0x20 , make_value (" FW Droop Slope (Kpower_Freq)" , " Pn%/Hz" , 10 ) },
81+ { 0x21 , make_value (" Recovery Ramp Rate (RRR)" , " Pn%/s" , 100 ) },
82+ { 0x22 , make_value (" Recovery High Frequency (RVHF)" , " Hz" , 100 ) },
83+ { 0x23 , make_value (" Recovery Low Frequency (RVLF)" , " Hz" , 100 ) },
84+ { 0x24 , make_value (" VW Function Activated" , " bool" , 1 ) },
85+ { 0x25 , make_value (" Start of Voltage Watt Droop (Vstart)" , " V" , 10 ) },
86+ { 0x26 , make_value (" End of Voltage Watt Droop (Vend)" , " V" , 10 ) },
87+ { 0x27 , make_value (" Droop Slope (Kpower_Volt)" , " Pn%/V" , 100 ) },
88+ { 0x28 , make_value (" APC Function Activated" , " bool" , 1 ) },
89+ { 0x29 , make_value (" Power Ramp Rate (PRR)" , " Pn%/s" , 100 ) },
90+ { 0x2a , make_value (" VV Function Activated" , " bool" , 1 ) },
91+ { 0x2b , make_value (" Voltage Set Point V1" , " V" , 10 ) },
92+ { 0x2c , make_value (" Reactive Set Point Q1" , " %Pn" , 10 ) },
93+ { 0x2d , make_value (" Voltage Set Point V2" , " V" , 10 ) },
94+ { 0x2e , make_value (" Voltage Set Point V3" , " V" , 10 ) },
95+ { 0x2f , make_value (" Voltage Set Point V4" , " V" , 10 ) },
96+ { 0x30 , make_value (" Reactive Set Point Q4" , " %Pn" , 10 ) },
97+ { 0x31 , make_value (" Setting Time (Tr)" , " s" , 10 ) },
98+ { 0x32 , make_value (" SPF Function Activated" , " bool" , 1 ) },
99+ { 0x33 , make_value (" Power Factor (PF)" , " " , 100 ) },
100+ { 0x34 , make_value (" RPC Function Activated" , " bool" , 1 ) },
101+ { 0x35 , make_value (" Reactive Power (VAR)" , " %Sn" , 1 ) },
102+ { 0x36 , make_value (" WPF Function Activated" , " bool" , 1 ) },
103+ { 0x37 , make_value (" Start of Power of WPF (Pstart)" , " %Pn" , 10 ) },
104+ { 0x38 , make_value (" Power Factor ar Rated Power (PFRP)" , " " , 100 ) },
105+ };
106+
107+ const std::array<const GridProfileValue_t, SECTION_VALUE_COUNT> GridProfileParser::_profile_values = { {
108+ // Voltage (H/LVRT)
109+ // Version 0x00
110+ { 0x00 , 0x00 , 0x01 },
111+ { 0x00 , 0x00 , 0x02 },
112+ { 0x00 , 0x00 , 0x03 },
113+ { 0x00 , 0x00 , 0x04 },
114+ { 0x00 , 0x00 , 0x05 },
115+
116+ // Version 0x03
117+ { 0x00 , 0x03 , 0x01 },
118+ { 0x00 , 0x03 , 0x02 },
119+ { 0x00 , 0x03 , 0x03 },
120+ { 0x00 , 0x03 , 0x05 },
121+ { 0x00 , 0x03 , 0x06 },
122+ { 0x00 , 0x03 , 0x07 },
123+ { 0x00 , 0x03 , 0x08 },
124+ { 0x00 , 0x03 , 0x09 },
125+
126+ // Version 0x0a
127+ { 0x00 , 0x0a , 0x01 },
128+ { 0x00 , 0x0a , 0x02 },
129+ { 0x00 , 0x0a , 0x03 },
130+ { 0x00 , 0x0a , 0x04 },
131+ { 0x00 , 0x0a , 0x05 },
132+ { 0x00 , 0x0a , 0x06 },
133+ { 0x00 , 0x0a , 0x07 },
134+ { 0x00 , 0x0a , 0x0a },
135+
136+ // Version 0x0b
137+ { 0x00 , 0x0b , 0x01 },
138+ { 0x00 , 0x0b , 0x02 },
139+ { 0x00 , 0x0b , 0x03 },
140+ { 0x00 , 0x0b , 0x04 },
141+ { 0x00 , 0x0b , 0x05 },
142+ { 0x00 , 0x0b , 0x06 },
143+ { 0x00 , 0x0b , 0x07 },
144+ { 0x00 , 0x0b , 0x08 },
145+ { 0x00 , 0x0b , 0x09 },
146+ { 0x00 , 0x0b , 0x0a },
147+
148+ // Version 0x0c
149+ { 0x00 , 0x0c , 0x01 },
150+ { 0x00 , 0x0c , 0x02 },
151+ { 0x00 , 0x0c , 0x03 },
152+ { 0x00 , 0x0c , 0x04 },
153+ { 0x00 , 0x0c , 0x05 },
154+ { 0x00 , 0x0c , 0x06 },
155+ { 0x00 , 0x0c , 0x07 },
156+ { 0x00 , 0x0c , 0x08 },
157+ { 0x00 , 0x0c , 0x09 },
158+ { 0x00 , 0x0c , 0x0b },
159+ { 0x00 , 0x0c , 0x0c },
160+ { 0x00 , 0x0c , 0x0a },
161+
162+ // Frequency (H/LFRT)
163+ // Version 0x00
164+ { 0x10 , 0x00 , 0x0d },
165+ { 0x10 , 0x00 , 0x0e },
166+ { 0x10 , 0x00 , 0x0f },
167+ { 0x10 , 0x00 , 0x10 },
168+ { 0x10 , 0x00 , 0x11 },
169+
170+ // Version 0x03
171+ { 0x10 , 0x03 , 0x0d },
172+ { 0x10 , 0x03 , 0x0e },
173+ { 0x10 , 0x03 , 0x0f },
174+ { 0x10 , 0x03 , 0x10 },
175+ { 0x10 , 0x03 , 0x11 },
176+ { 0x10 , 0x03 , 0x12 },
177+ { 0x10 , 0x03 , 0x13 },
178+ { 0x10 , 0x03 , 0x14 },
179+ { 0x10 , 0x03 , 0x15 },
180+
181+ // Island Detection (ID)
182+ // Version 0x00
183+ { 0x20 , 0x00 , 0x16 },
184+
185+ // Reconnection (RT)
186+ // Version 0x03
187+ { 0x30 , 0x03 , 0x17 },
188+ { 0x30 , 0x03 , 0x18 },
189+ { 0x30 , 0x03 , 0x19 },
190+ { 0x30 , 0x03 , 0x1a },
191+ { 0x30 , 0x03 , 0x1b },
192+
193+ // Ramp Rates (RR)
194+ // Version 0x00
195+ { 0x40 , 0x00 , 0x1c },
196+ { 0x40 , 0x00 , 0x1d },
197+
198+ // Frequency Watt (FW)
199+ // Version 0x00
200+ { 0x50 , 0x00 , 0x1e },
201+ { 0x50 , 0x00 , 0x1f },
202+ { 0x50 , 0x00 , 0x20 },
203+ { 0x50 , 0x00 , 0x21 },
204+
205+ // Version 0x01
206+ { 0x50 , 0x01 , 0x1e },
207+ { 0x50 , 0x01 , 0x1f },
208+ { 0x50 , 0x01 , 0x20 },
209+ { 0x50 , 0x01 , 0x21 },
210+ { 0x50 , 0x01 , 0x22 },
211+
212+ // Version 0x08
213+ { 0x50 , 0x08 , 0x1e },
214+ { 0x50 , 0x08 , 0x1f },
215+ { 0x50 , 0x08 , 0x20 },
216+ { 0x50 , 0x08 , 0x21 },
217+ { 0x50 , 0x08 , 0x22 },
218+ { 0x50 , 0x08 , 0x23 },
219+
220+ // Volt Watt (VW)
221+ // Version 0x00
222+ { 0x60 , 0x00 , 0x24 },
223+ { 0x60 , 0x00 , 0x25 },
224+ { 0x60 , 0x00 , 0x26 },
225+ { 0x60 , 0x00 , 0x27 },
226+
227+ // Version 0x04
228+ { 0x60 , 0x04 , 0x24 },
229+ { 0x60 , 0x04 , 0x25 },
230+ { 0x60 , 0x04 , 0x26 },
231+ { 0x60 , 0x04 , 0x27 },
232+
233+ // Active Power Control (APC)
234+ // Version 0x00
235+ { 0x70 , 0x00 , 0x28 },
236+
237+ // Version 0x02
238+ { 0x70 , 0x02 , 0x28 },
239+ { 0x70 , 0x02 , 0x29 },
240+
241+ // Volt Var (VV)
242+ // Version 0x00
243+ { 0x80 , 0x00 , 0x2a },
244+ { 0x80 , 0x00 , 0x2b },
245+ { 0x80 , 0x00 , 0x2c },
246+ { 0x80 , 0x00 , 0x2d },
247+ { 0x80 , 0x00 , 0x2e },
248+ { 0x80 , 0x00 , 0x2f },
249+ { 0x80 , 0x00 , 0x30 },
250+
251+ // Version 0x01
252+ { 0x80 , 0x01 , 0x2a },
253+ { 0x80 , 0x01 , 0x2b },
254+ { 0x80 , 0x01 , 0x2c },
255+ { 0x80 , 0x01 , 0x2d },
256+ { 0x80 , 0x01 , 0x2e },
257+ { 0x80 , 0x01 , 0x2f },
258+ { 0x80 , 0x01 , 0x30 },
259+ { 0x80 , 0x01 , 0x31 },
260+
261+ // Specified Power Factor (SPF)
262+ // Version 0x00
263+ { 0x90 , 0x00 , 0x32 },
264+ { 0x90 , 0x00 , 0x33 },
265+
266+ // Reactive Power Control (RPC)
267+ // Version 0x02
268+ { 0xa0 , 0x02 , 0x34 },
269+ { 0xa0 , 0x02 , 0x35 },
270+
271+ // Watt Power Factor (WPF)
272+ // Version 0x00
273+ { 0xb0 , 0x00 , 0x36 },
274+ { 0xb0 , 0x00 , 0x37 },
275+ { 0xb0 , 0x00 , 0x38 },
276+ } };
277+
19278GridProfileParser::GridProfileParser ()
20279 : Parser()
21280{
@@ -51,7 +310,9 @@ String GridProfileParser::getProfileName()
51310String GridProfileParser::getProfileVersion ()
52311{
53312 char buffer[10 ];
313+ HOY_SEMAPHORE_TAKE ();
54314 snprintf (buffer, sizeof (buffer), " %d.%d.%d" , (_payloadGridProfile[2 ] >> 4 ) & 0x0f , _payloadGridProfile[2 ] & 0x0f , _payloadGridProfile[3 ]);
315+ HOY_SEMAPHORE_GIVE ();
55316 return buffer;
56317}
57318
@@ -65,3 +326,70 @@ std::vector<uint8_t> GridProfileParser::getRawData()
65326 HOY_SEMAPHORE_GIVE ();
66327 return ret;
67328}
329+
330+ std::list<GridProfileSection_t> GridProfileParser::getProfile ()
331+ {
332+ std::list<GridProfileSection_t> l;
333+
334+ if (_gridProfileLength > 4 ) {
335+ uint16_t pos = 4 ;
336+ do {
337+ uint8_t section_id = _payloadGridProfile[pos];
338+ uint8_t section_version = _payloadGridProfile[pos + 1 ];
339+ int8_t section_start = getSectionStart (section_id, section_version);
340+ uint8_t section_size = getSectionSize (section_id, section_version);
341+ pos += 2 ;
342+
343+ GridProfileSection_t section;
344+ try {
345+ section.SectionName = profile_section.at (section_id).data ();
346+ } catch (const std::out_of_range&) {
347+ section.SectionName = " Unknown" ;
348+ break ;
349+ }
350+
351+ for (uint8_t val_id = 0 ; val_id < section_size; val_id++) {
352+ auto value_setting = value_names.at (_profile_values[section_start + val_id].Type );
353+
354+ float value = (_payloadGridProfile[pos] << 8 ) | _payloadGridProfile[pos + 1 ];
355+ value /= value_setting.Dividor ;
356+
357+ GridProfileItem_t v;
358+ v.Name = value_setting.Name .data ();
359+ v.Unit = value_setting.Unit .data ();
360+ v.Value = value;
361+ section.items .push_back (v);
362+
363+ pos += 2 ;
364+ }
365+
366+ l.push_back (section);
367+
368+ } while (pos < _gridProfileLength);
369+ }
370+
371+ return l;
372+ }
373+
374+ uint8_t GridProfileParser::getSectionSize (uint8_t section_id, uint8_t section_version)
375+ {
376+ uint8_t count = 0 ;
377+ for (auto & values : _profile_values) {
378+ if (values.Section == section_id && values.Version == section_version) {
379+ count++;
380+ }
381+ }
382+ return count;
383+ }
384+
385+ int8_t GridProfileParser::getSectionStart (uint8_t section_id, uint8_t section_version)
386+ {
387+ uint8_t count = -1 ;
388+ for (auto & values : _profile_values) {
389+ count++;
390+ if (values.Section == section_id && values.Version == section_version) {
391+ break ;
392+ }
393+ }
394+ return count;
395+ }
0 commit comments