1 /**
2  * Copyright © 2020 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "config_file_parser.hpp"
18 
19 #include "config_file_parser_error.hpp"
20 #include "i2c_interface.hpp"
21 #include "pmbus_utils.hpp"
22 
23 #include <exception>
24 #include <fstream>
25 #include <optional>
26 #include <utility>
27 
28 using json = nlohmann::json;
29 
30 namespace phosphor::power::regulators::config_file_parser
31 {
32 
33 std::tuple<std::vector<std::unique_ptr<Rule>>,
34            std::vector<std::unique_ptr<Chassis>>>
35     parse(const std::filesystem::path& pathName)
36 {
37     try
38     {
39         // Use standard JSON parser to create tree of JSON elements
40         std::ifstream file{pathName};
41         json rootElement = json::parse(file);
42 
43         // Parse tree of JSON elements and return corresponding C++ objects
44         return internal::parseRoot(rootElement);
45     }
46     catch (const std::exception& e)
47     {
48         throw ConfigFileParserError{pathName, e.what()};
49     }
50 }
51 
52 namespace internal
53 {
54 
55 std::unique_ptr<Action> parseAction(const json& element)
56 {
57     verifyIsObject(element);
58     unsigned int propertyCount{0};
59 
60     // Optional comments property; value not stored
61     if (element.contains("comments"))
62     {
63         ++propertyCount;
64     }
65 
66     // Required action type property; there must be exactly one specified
67     std::unique_ptr<Action> action{};
68     if (element.contains("and"))
69     {
70         action = parseAnd(element["and"]);
71         ++propertyCount;
72     }
73     else if (element.contains("compare_presence"))
74     {
75         action = parseComparePresence(element["compare_presence"]);
76         ++propertyCount;
77     }
78     else if (element.contains("compare_vpd"))
79     {
80         action = parseCompareVPD(element["compare_vpd"]);
81         ++propertyCount;
82     }
83     else if (element.contains("i2c_compare_bit"))
84     {
85         action = parseI2CCompareBit(element["i2c_compare_bit"]);
86         ++propertyCount;
87     }
88     else if (element.contains("i2c_compare_byte"))
89     {
90         action = parseI2CCompareByte(element["i2c_compare_byte"]);
91         ++propertyCount;
92     }
93     else if (element.contains("i2c_compare_bytes"))
94     {
95         action = parseI2CCompareBytes(element["i2c_compare_bytes"]);
96         ++propertyCount;
97     }
98     else if (element.contains("i2c_write_bit"))
99     {
100         action = parseI2CWriteBit(element["i2c_write_bit"]);
101         ++propertyCount;
102     }
103     else if (element.contains("i2c_write_byte"))
104     {
105         action = parseI2CWriteByte(element["i2c_write_byte"]);
106         ++propertyCount;
107     }
108     else if (element.contains("i2c_write_bytes"))
109     {
110         action = parseI2CWriteBytes(element["i2c_write_bytes"]);
111         ++propertyCount;
112     }
113     else if (element.contains("if"))
114     {
115         action = parseIf(element["if"]);
116         ++propertyCount;
117     }
118     else if (element.contains("not"))
119     {
120         action = parseNot(element["not"]);
121         ++propertyCount;
122     }
123     else if (element.contains("or"))
124     {
125         action = parseOr(element["or"]);
126         ++propertyCount;
127     }
128     else if (element.contains("pmbus_read_sensor"))
129     {
130         action = parsePMBusReadSensor(element["pmbus_read_sensor"]);
131         ++propertyCount;
132     }
133     else if (element.contains("pmbus_write_vout_command"))
134     {
135         action =
136             parsePMBusWriteVoutCommand(element["pmbus_write_vout_command"]);
137         ++propertyCount;
138     }
139     else if (element.contains("run_rule"))
140     {
141         action = parseRunRule(element["run_rule"]);
142         ++propertyCount;
143     }
144     else if (element.contains("set_device"))
145     {
146         action = parseSetDevice(element["set_device"]);
147         ++propertyCount;
148     }
149     else
150     {
151         throw std::invalid_argument{"Required action type property missing"};
152     }
153 
154     // Verify no invalid properties exist
155     verifyPropertyCount(element, propertyCount);
156 
157     return action;
158 }
159 
160 std::vector<std::unique_ptr<Action>> parseActionArray(const json& element)
161 {
162     verifyIsArray(element);
163     std::vector<std::unique_ptr<Action>> actions;
164     for (auto& actionElement : element)
165     {
166         actions.emplace_back(parseAction(actionElement));
167     }
168     return actions;
169 }
170 
171 std::unique_ptr<AndAction> parseAnd(const json& element)
172 {
173     verifyIsArray(element);
174 
175     // Verify if array size less than 2
176     if (element.size() < 2)
177     {
178         throw std::invalid_argument{"Array must contain two or more actions"};
179     }
180     // Array of two or more actions
181     std::vector<std::unique_ptr<Action>> actions = parseActionArray(element);
182 
183     return std::make_unique<AndAction>(std::move(actions));
184 }
185 
186 std::unique_ptr<Chassis> parseChassis(const json& element)
187 {
188     verifyIsObject(element);
189     unsigned int propertyCount{0};
190 
191     // Optional comments property; value not stored
192     if (element.contains("comments"))
193     {
194         ++propertyCount;
195     }
196 
197     // Required number property
198     const json& numberElement = getRequiredProperty(element, "number");
199     unsigned int number = parseUnsignedInteger(numberElement);
200     if (number < 1)
201     {
202         throw std::invalid_argument{"Invalid chassis number: Must be > 0"};
203     }
204     ++propertyCount;
205 
206     // Optional devices property
207     std::vector<std::unique_ptr<Device>> devices{};
208     auto devicesIt = element.find("devices");
209     if (devicesIt != element.end())
210     {
211         devices = parseDeviceArray(*devicesIt);
212         ++propertyCount;
213     }
214 
215     // Verify no invalid properties exist
216     verifyPropertyCount(element, propertyCount);
217 
218     return std::make_unique<Chassis>(number, std::move(devices));
219 }
220 
221 std::vector<std::unique_ptr<Chassis>> parseChassisArray(const json& element)
222 {
223     verifyIsArray(element);
224     std::vector<std::unique_ptr<Chassis>> chassis;
225     for (auto& chassisElement : element)
226     {
227         chassis.emplace_back(parseChassis(chassisElement));
228     }
229     return chassis;
230 }
231 
232 std::unique_ptr<ComparePresenceAction> parseComparePresence(const json& element)
233 {
234     verifyIsObject(element);
235     unsigned int propertyCount{0};
236 
237     // Required fru property
238     const json& fruElement = getRequiredProperty(element, "fru");
239     std::string fru = parseInventoryPath(fruElement);
240     ++propertyCount;
241 
242     // Required value property
243     const json& valueElement = getRequiredProperty(element, "value");
244     bool value = parseBoolean(valueElement);
245     ++propertyCount;
246 
247     // Verify no invalid properties exist
248     verifyPropertyCount(element, propertyCount);
249 
250     return std::make_unique<ComparePresenceAction>(fru, value);
251 }
252 
253 std::unique_ptr<CompareVPDAction> parseCompareVPD(const json& element)
254 {
255     verifyIsObject(element);
256     unsigned int propertyCount{0};
257 
258     // Required fru property
259     const json& fruElement = getRequiredProperty(element, "fru");
260     std::string fru = parseInventoryPath(fruElement);
261     ++propertyCount;
262 
263     // Required keyword property
264     const json& keywordElement = getRequiredProperty(element, "keyword");
265     std::string keyword = parseString(keywordElement);
266     ++propertyCount;
267 
268     // Required value property
269     const json& valueElement = getRequiredProperty(element, "value");
270     std::string value = parseString(valueElement);
271     ++propertyCount;
272 
273     // Verify no invalid properties exist
274     verifyPropertyCount(element, propertyCount);
275 
276     return std::make_unique<CompareVPDAction>(fru, keyword, value);
277 }
278 
279 std::unique_ptr<Configuration> parseConfiguration(const json& element)
280 {
281     verifyIsObject(element);
282     unsigned int propertyCount{0};
283 
284     // Optional comments property; value not stored
285     if (element.contains("comments"))
286     {
287         ++propertyCount;
288     }
289 
290     // Optional volts property
291     std::optional<double> volts{};
292     auto voltsIt = element.find("volts");
293     if (voltsIt != element.end())
294     {
295         volts = parseDouble(*voltsIt);
296         ++propertyCount;
297     }
298 
299     // Required rule_id or actions property
300     std::vector<std::unique_ptr<Action>> actions{};
301     actions = parseRuleIDOrActionsProperty(element);
302     ++propertyCount;
303 
304     // Verify no invalid properties exist
305     verifyPropertyCount(element, propertyCount);
306 
307     return std::make_unique<Configuration>(volts, std::move(actions));
308 }
309 
310 std::unique_ptr<Device> parseDevice(const json& element)
311 {
312     verifyIsObject(element);
313     unsigned int propertyCount{0};
314 
315     // Optional comments property; value not stored
316     if (element.contains("comments"))
317     {
318         ++propertyCount;
319     }
320 
321     // Required id property
322     const json& idElement = getRequiredProperty(element, "id");
323     std::string id = parseString(idElement);
324     ++propertyCount;
325 
326     // Required is_regulator property
327     const json& isRegulatorElement =
328         getRequiredProperty(element, "is_regulator");
329     bool isRegulator = parseBoolean(isRegulatorElement);
330     ++propertyCount;
331 
332     // Required fru property
333     const json& fruElement = getRequiredProperty(element, "fru");
334     std::string fru = parseInventoryPath(fruElement);
335     ++propertyCount;
336 
337     // Required i2c_interface property
338     const json& i2cInterfaceElement =
339         getRequiredProperty(element, "i2c_interface");
340     std::unique_ptr<i2c::I2CInterface> i2cInterface =
341         parseI2CInterface(i2cInterfaceElement);
342     ++propertyCount;
343 
344     // Optional presence_detection property
345     std::unique_ptr<PresenceDetection> presenceDetection{};
346     auto presenceDetectionIt = element.find("presence_detection");
347     if (presenceDetectionIt != element.end())
348     {
349         presenceDetection = parsePresenceDetection(*presenceDetectionIt);
350         ++propertyCount;
351     }
352 
353     // Optional configuration property
354     std::unique_ptr<Configuration> configuration{};
355     auto configurationIt = element.find("configuration");
356     if (configurationIt != element.end())
357     {
358         configuration = parseConfiguration(*configurationIt);
359         ++propertyCount;
360     }
361 
362     // Optional rails property
363     std::vector<std::unique_ptr<Rail>> rails{};
364     auto railsIt = element.find("rails");
365     if (railsIt != element.end())
366     {
367         if (!isRegulator)
368         {
369             throw std::invalid_argument{
370                 "Invalid rails property when is_regulator is false"};
371         }
372         rails = parseRailArray(*railsIt);
373         ++propertyCount;
374     }
375 
376     // Verify no invalid properties exist
377     verifyPropertyCount(element, propertyCount);
378 
379     return std::make_unique<Device>(id, isRegulator, fru,
380                                     std::move(i2cInterface),
381                                     std::move(presenceDetection),
382                                     std::move(configuration), std::move(rails));
383 }
384 
385 std::vector<std::unique_ptr<Device>> parseDeviceArray(const json& element)
386 {
387     verifyIsArray(element);
388     std::vector<std::unique_ptr<Device>> devices;
389     for (auto& deviceElement : element)
390     {
391         devices.emplace_back(parseDevice(deviceElement));
392     }
393     return devices;
394 }
395 
396 std::vector<uint8_t> parseHexByteArray(const json& element)
397 {
398     verifyIsArray(element);
399     std::vector<uint8_t> values;
400     for (auto& valueElement : element)
401     {
402         values.emplace_back(parseHexByte(valueElement));
403     }
404     return values;
405 }
406 
407 std::unique_ptr<I2CCompareBitAction> parseI2CCompareBit(const json& element)
408 {
409     verifyIsObject(element);
410     unsigned int propertyCount{0};
411 
412     // Required register property
413     const json& regElement = getRequiredProperty(element, "register");
414     uint8_t reg = parseHexByte(regElement);
415     ++propertyCount;
416 
417     // Required position property
418     const json& positionElement = getRequiredProperty(element, "position");
419     uint8_t position = parseBitPosition(positionElement);
420     ++propertyCount;
421 
422     // Required value property
423     const json& valueElement = getRequiredProperty(element, "value");
424     uint8_t value = parseBitValue(valueElement);
425     ++propertyCount;
426 
427     // Verify no invalid properties exist
428     verifyPropertyCount(element, propertyCount);
429 
430     return std::make_unique<I2CCompareBitAction>(reg, position, value);
431 }
432 
433 std::unique_ptr<I2CCompareByteAction> parseI2CCompareByte(const json& element)
434 {
435     verifyIsObject(element);
436     unsigned int propertyCount{0};
437 
438     // Required register property
439     const json& regElement = getRequiredProperty(element, "register");
440     uint8_t reg = parseHexByte(regElement);
441     ++propertyCount;
442 
443     // Required value property
444     const json& valueElement = getRequiredProperty(element, "value");
445     uint8_t value = parseHexByte(valueElement);
446     ++propertyCount;
447 
448     // Optional mask property
449     uint8_t mask = 0xff;
450     auto maskIt = element.find("mask");
451     if (maskIt != element.end())
452     {
453         mask = parseHexByte(*maskIt);
454         ++propertyCount;
455     }
456 
457     // Verify no invalid properties exist
458     verifyPropertyCount(element, propertyCount);
459 
460     return std::make_unique<I2CCompareByteAction>(reg, value, mask);
461 }
462 
463 std::unique_ptr<I2CCompareBytesAction> parseI2CCompareBytes(const json& element)
464 {
465     verifyIsObject(element);
466     unsigned int propertyCount{0};
467 
468     // Required register property
469     const json& regElement = getRequiredProperty(element, "register");
470     uint8_t reg = parseHexByte(regElement);
471     ++propertyCount;
472 
473     // Required values property
474     const json& valueElement = getRequiredProperty(element, "values");
475     std::vector<uint8_t> values = parseHexByteArray(valueElement);
476     ++propertyCount;
477 
478     // Optional masks property
479     std::vector<uint8_t> masks{};
480     auto masksIt = element.find("masks");
481     if (masksIt != element.end())
482     {
483         masks = parseHexByteArray(*masksIt);
484         ++propertyCount;
485     }
486 
487     // Verify masks array (if specified) was same size as values array
488     if ((!masks.empty()) && (masks.size() != values.size()))
489     {
490         throw std::invalid_argument{"Invalid number of elements in masks"};
491     }
492 
493     // Verify no invalid properties exist
494     verifyPropertyCount(element, propertyCount);
495 
496     if (masks.empty())
497     {
498         return std::make_unique<I2CCompareBytesAction>(reg, values);
499     }
500     return std::make_unique<I2CCompareBytesAction>(reg, values, masks);
501 }
502 
503 std::unique_ptr<i2c::I2CInterface> parseI2CInterface(const json& element)
504 {
505     verifyIsObject(element);
506     unsigned int propertyCount{0};
507 
508     // Required bus property
509     const json& busElement = getRequiredProperty(element, "bus");
510     uint8_t bus = parseUint8(busElement);
511     ++propertyCount;
512 
513     // Required address property
514     const json& addressElement = getRequiredProperty(element, "address");
515     uint8_t address = parseHexByte(addressElement);
516     ++propertyCount;
517 
518     verifyPropertyCount(element, propertyCount);
519     return i2c::create(bus, address, i2c::I2CInterface::InitialState::CLOSED);
520 }
521 
522 std::unique_ptr<I2CWriteBitAction> parseI2CWriteBit(const json& element)
523 {
524     verifyIsObject(element);
525     unsigned int propertyCount{0};
526 
527     // Required register property
528     const json& regElement = getRequiredProperty(element, "register");
529     uint8_t reg = parseHexByte(regElement);
530     ++propertyCount;
531 
532     // Required position property
533     const json& positionElement = getRequiredProperty(element, "position");
534     uint8_t position = parseBitPosition(positionElement);
535     ++propertyCount;
536 
537     // Required value property
538     const json& valueElement = getRequiredProperty(element, "value");
539     uint8_t value = parseBitValue(valueElement);
540     ++propertyCount;
541 
542     // Verify no invalid properties exist
543     verifyPropertyCount(element, propertyCount);
544 
545     return std::make_unique<I2CWriteBitAction>(reg, position, value);
546 }
547 
548 std::unique_ptr<I2CWriteByteAction> parseI2CWriteByte(const json& element)
549 {
550     verifyIsObject(element);
551     unsigned int propertyCount{0};
552 
553     // Required register property
554     const json& regElement = getRequiredProperty(element, "register");
555     uint8_t reg = parseHexByte(regElement);
556     ++propertyCount;
557 
558     // Required value property
559     const json& valueElement = getRequiredProperty(element, "value");
560     uint8_t value = parseHexByte(valueElement);
561     ++propertyCount;
562 
563     // Optional mask property
564     uint8_t mask = 0xff;
565     auto maskIt = element.find("mask");
566     if (maskIt != element.end())
567     {
568         mask = parseHexByte(*maskIt);
569         ++propertyCount;
570     }
571 
572     // Verify no invalid properties exist
573     verifyPropertyCount(element, propertyCount);
574 
575     return std::make_unique<I2CWriteByteAction>(reg, value, mask);
576 }
577 
578 std::unique_ptr<I2CWriteBytesAction> parseI2CWriteBytes(const json& element)
579 {
580     verifyIsObject(element);
581     unsigned int propertyCount{0};
582 
583     // Required register property
584     const json& regElement = getRequiredProperty(element, "register");
585     uint8_t reg = parseHexByte(regElement);
586     ++propertyCount;
587 
588     // Required values property
589     const json& valueElement = getRequiredProperty(element, "values");
590     std::vector<uint8_t> values = parseHexByteArray(valueElement);
591     ++propertyCount;
592 
593     // Optional masks property
594     std::vector<uint8_t> masks{};
595     auto masksIt = element.find("masks");
596     if (masksIt != element.end())
597     {
598         masks = parseHexByteArray(*masksIt);
599         ++propertyCount;
600     }
601 
602     // Verify masks array (if specified) was same size as values array
603     if ((!masks.empty()) && (masks.size() != values.size()))
604     {
605         throw std::invalid_argument{"Invalid number of elements in masks"};
606     }
607 
608     // Verify no invalid properties exist
609     verifyPropertyCount(element, propertyCount);
610 
611     if (masks.empty())
612     {
613         return std::make_unique<I2CWriteBytesAction>(reg, values);
614     }
615     return std::make_unique<I2CWriteBytesAction>(reg, values, masks);
616 }
617 
618 std::unique_ptr<IfAction> parseIf(const json& element)
619 {
620     verifyIsObject(element);
621     unsigned int propertyCount{0};
622 
623     // Required condition property
624     const json& conditionElement = getRequiredProperty(element, "condition");
625     std::unique_ptr<Action> conditionAction = parseAction(conditionElement);
626     ++propertyCount;
627 
628     // Required then property
629     const json& thenElement = getRequiredProperty(element, "then");
630     std::vector<std::unique_ptr<Action>> thenActions =
631         parseActionArray(thenElement);
632     ++propertyCount;
633 
634     // Optional else property
635     std::vector<std::unique_ptr<Action>> elseActions{};
636     auto elseIt = element.find("else");
637     if (elseIt != element.end())
638     {
639         elseActions = parseActionArray(*elseIt);
640         ++propertyCount;
641     }
642 
643     // Verify no invalid properties exist
644     verifyPropertyCount(element, propertyCount);
645 
646     return std::make_unique<IfAction>(std::move(conditionAction),
647                                       std::move(thenActions),
648                                       std::move(elseActions));
649 }
650 
651 std::string parseInventoryPath(const json& element)
652 {
653     std::string inventoryPath = parseString(element);
654     std::string absPath = "/xyz/openbmc_project/inventory";
655     if (inventoryPath.front() != '/')
656     {
657         absPath += '/';
658     }
659     absPath += inventoryPath;
660     return absPath;
661 }
662 
663 std::unique_ptr<NotAction> parseNot(const json& element)
664 {
665     // Required action to execute
666     std::unique_ptr<Action> action = parseAction(element);
667 
668     return std::make_unique<NotAction>(std::move(action));
669 }
670 
671 std::unique_ptr<OrAction> parseOr(const json& element)
672 {
673     verifyIsArray(element);
674 
675     // Verify if array size less than 2
676     if (element.size() < 2)
677     {
678         throw std::invalid_argument{"Array must contain two or more actions"};
679     }
680     // Array of two or more actions
681     std::vector<std::unique_ptr<Action>> actions = parseActionArray(element);
682 
683     return std::make_unique<OrAction>(std::move(actions));
684 }
685 
686 std::unique_ptr<PMBusReadSensorAction> parsePMBusReadSensor(const json& element)
687 {
688     verifyIsObject(element);
689     unsigned int propertyCount{0};
690 
691     // Required type property
692     const json& typeElement = getRequiredProperty(element, "type");
693     pmbus_utils::SensorValueType type = parseSensorValueType(typeElement);
694     ++propertyCount;
695 
696     // Required command property
697     const json& commandElement = getRequiredProperty(element, "command");
698     uint8_t command = parseHexByte(commandElement);
699     ++propertyCount;
700 
701     // Required format property
702     const json& formatElement = getRequiredProperty(element, "format");
703     pmbus_utils::SensorDataFormat format = parseSensorDataFormat(formatElement);
704     ++propertyCount;
705 
706     // Optional exponent property
707     std::optional<int8_t> exponent{};
708     auto exponentIt = element.find("exponent");
709     if (exponentIt != element.end())
710     {
711         exponent = parseInt8(*exponentIt);
712         ++propertyCount;
713     }
714 
715     // Verify no invalid properties exist
716     verifyPropertyCount(element, propertyCount);
717 
718     return std::make_unique<PMBusReadSensorAction>(type, command, format,
719                                                    exponent);
720 }
721 
722 std::unique_ptr<PMBusWriteVoutCommandAction>
723     parsePMBusWriteVoutCommand(const json& element)
724 {
725     verifyIsObject(element);
726     unsigned int propertyCount{0};
727 
728     // Optional volts property
729     std::optional<double> volts{};
730     auto voltsIt = element.find("volts");
731     if (voltsIt != element.end())
732     {
733         volts = parseDouble(*voltsIt);
734         ++propertyCount;
735     }
736 
737     // Required format property
738     const json& formatElement = getRequiredProperty(element, "format");
739     std::string formatString = parseString(formatElement);
740     if (formatString != "linear")
741     {
742         throw std::invalid_argument{"Invalid format value: " + formatString};
743     }
744     pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::linear;
745     ++propertyCount;
746 
747     // Optional exponent property
748     std::optional<int8_t> exponent{};
749     auto exponentIt = element.find("exponent");
750     if (exponentIt != element.end())
751     {
752         exponent = parseInt8(*exponentIt);
753         ++propertyCount;
754     }
755 
756     // Optional is_verified property
757     bool isVerified = false;
758     auto isVerifiedIt = element.find("is_verified");
759     if (isVerifiedIt != element.end())
760     {
761         isVerified = parseBoolean(*isVerifiedIt);
762         ++propertyCount;
763     }
764 
765     // Verify no invalid properties exist
766     verifyPropertyCount(element, propertyCount);
767 
768     return std::make_unique<PMBusWriteVoutCommandAction>(volts, format,
769                                                          exponent, isVerified);
770 }
771 
772 std::unique_ptr<PresenceDetection> parsePresenceDetection(const json& element)
773 {
774     verifyIsObject(element);
775     unsigned int propertyCount{0};
776 
777     // Optional comments property; value not stored
778     if (element.contains("comments"))
779     {
780         ++propertyCount;
781     }
782 
783     // Required rule_id or actions property
784     std::vector<std::unique_ptr<Action>> actions{};
785     actions = parseRuleIDOrActionsProperty(element);
786     ++propertyCount;
787 
788     // Verify no invalid properties exist
789     verifyPropertyCount(element, propertyCount);
790 
791     return std::make_unique<PresenceDetection>(std::move(actions));
792 }
793 
794 std::unique_ptr<Rail> parseRail(const json& element)
795 {
796     verifyIsObject(element);
797     unsigned int propertyCount{0};
798 
799     // Optional comments property; value not stored
800     if (element.contains("comments"))
801     {
802         ++propertyCount;
803     }
804 
805     // Required id property
806     const json& idElement = getRequiredProperty(element, "id");
807     std::string id = parseString(idElement);
808     ++propertyCount;
809 
810     // Optional configuration property
811     std::unique_ptr<Configuration> configuration{};
812     auto configurationIt = element.find("configuration");
813     if (configurationIt != element.end())
814     {
815         configuration = parseConfiguration(*configurationIt);
816         ++propertyCount;
817     }
818 
819     // Optional sensor_monitoring property
820     std::unique_ptr<SensorMonitoring> sensorMonitoring{};
821     auto sensorMonitoringIt = element.find("sensor_monitoring");
822     if (sensorMonitoringIt != element.end())
823     {
824         sensorMonitoring = parseSensorMonitoring(*sensorMonitoringIt);
825         ++propertyCount;
826     }
827 
828     // Verify no invalid properties exist
829     verifyPropertyCount(element, propertyCount);
830 
831     return std::make_unique<Rail>(id, std::move(configuration),
832                                   std::move(sensorMonitoring));
833 }
834 
835 std::vector<std::unique_ptr<Rail>> parseRailArray(const json& element)
836 {
837     verifyIsArray(element);
838     std::vector<std::unique_ptr<Rail>> rails;
839     for (auto& railElement : element)
840     {
841         rails.emplace_back(parseRail(railElement));
842     }
843     return rails;
844 }
845 
846 std::tuple<std::vector<std::unique_ptr<Rule>>,
847            std::vector<std::unique_ptr<Chassis>>>
848     parseRoot(const json& element)
849 {
850     verifyIsObject(element);
851     unsigned int propertyCount{0};
852 
853     // Optional comments property; value not stored
854     if (element.contains("comments"))
855     {
856         ++propertyCount;
857     }
858 
859     // Optional rules property
860     std::vector<std::unique_ptr<Rule>> rules{};
861     auto rulesIt = element.find("rules");
862     if (rulesIt != element.end())
863     {
864         rules = parseRuleArray(*rulesIt);
865         ++propertyCount;
866     }
867 
868     // Required chassis property
869     const json& chassisElement = getRequiredProperty(element, "chassis");
870     std::vector<std::unique_ptr<Chassis>> chassis =
871         parseChassisArray(chassisElement);
872     ++propertyCount;
873 
874     // Verify no invalid properties exist
875     verifyPropertyCount(element, propertyCount);
876 
877     return std::make_tuple(std::move(rules), std::move(chassis));
878 }
879 
880 std::unique_ptr<Rule> parseRule(const json& element)
881 {
882     verifyIsObject(element);
883     unsigned int propertyCount{0};
884 
885     // Optional comments property; value not stored
886     if (element.contains("comments"))
887     {
888         ++propertyCount;
889     }
890 
891     // Required id property
892     const json& idElement = getRequiredProperty(element, "id");
893     std::string id = parseString(idElement);
894     ++propertyCount;
895 
896     // Required actions property
897     const json& actionsElement = getRequiredProperty(element, "actions");
898     std::vector<std::unique_ptr<Action>> actions =
899         parseActionArray(actionsElement);
900     ++propertyCount;
901 
902     // Verify no invalid properties exist
903     verifyPropertyCount(element, propertyCount);
904 
905     return std::make_unique<Rule>(id, std::move(actions));
906 }
907 
908 std::vector<std::unique_ptr<Rule>> parseRuleArray(const json& element)
909 {
910     verifyIsArray(element);
911     std::vector<std::unique_ptr<Rule>> rules;
912     for (auto& ruleElement : element)
913     {
914         rules.emplace_back(parseRule(ruleElement));
915     }
916     return rules;
917 }
918 
919 std::vector<std::unique_ptr<Action>>
920     parseRuleIDOrActionsProperty(const json& element)
921 {
922     verifyIsObject(element);
923     // Required rule_id or actions property
924     std::vector<std::unique_ptr<Action>> actions{};
925     auto ruleIDIt = element.find("rule_id");
926     auto actionsIt = element.find("actions");
927     if ((actionsIt == element.end()) && (ruleIDIt != element.end()))
928     {
929         std::string ruleID = parseString(*ruleIDIt);
930         actions.emplace_back(std::make_unique<RunRuleAction>(ruleID));
931     }
932     else if ((actionsIt != element.end()) && (ruleIDIt == element.end()))
933     {
934         actions = parseActionArray(*actionsIt);
935     }
936     else
937     {
938         throw std::invalid_argument{"Invalid property combination: Must "
939                                     "contain either rule_id or actions"};
940     }
941 
942     return actions;
943 }
944 
945 std::unique_ptr<RunRuleAction> parseRunRule(const json& element)
946 {
947     // String ruleID
948     std::string ruleID = parseString(element);
949 
950     return std::make_unique<RunRuleAction>(ruleID);
951 }
952 
953 pmbus_utils::SensorDataFormat parseSensorDataFormat(const json& element)
954 {
955     if (!element.is_string())
956     {
957         throw std::invalid_argument{"Element is not a string"};
958     }
959     std::string value = element.get<std::string>();
960     pmbus_utils::SensorDataFormat format{};
961 
962     if (value == "linear_11")
963     {
964         format = pmbus_utils::SensorDataFormat::linear_11;
965     }
966     else if (value == "linear_16")
967     {
968         format = pmbus_utils::SensorDataFormat::linear_16;
969     }
970     else
971     {
972         throw std::invalid_argument{"Element is not a sensor data format"};
973     }
974 
975     return format;
976 }
977 
978 std::unique_ptr<SensorMonitoring> parseSensorMonitoring(const json& element)
979 {
980     verifyIsObject(element);
981     unsigned int propertyCount{0};
982 
983     // Optional comments property; value not stored
984     if (element.contains("comments"))
985     {
986         ++propertyCount;
987     }
988 
989     // Required rule_id or actions property
990     std::vector<std::unique_ptr<Action>> actions{};
991     actions = parseRuleIDOrActionsProperty(element);
992     ++propertyCount;
993 
994     // Verify no invalid properties exist
995     verifyPropertyCount(element, propertyCount);
996 
997     return std::make_unique<SensorMonitoring>(std::move(actions));
998 }
999 
1000 pmbus_utils::SensorValueType parseSensorValueType(const json& element)
1001 {
1002     if (!element.is_string())
1003     {
1004         throw std::invalid_argument{"Element is not a string"};
1005     }
1006     std::string value = element.get<std::string>();
1007     pmbus_utils::SensorValueType type{};
1008 
1009     if (value == "iout")
1010     {
1011         type = pmbus_utils::SensorValueType::iout;
1012     }
1013     else if (value == "iout_peak")
1014     {
1015         type = pmbus_utils::SensorValueType::iout_peak;
1016     }
1017     else if (value == "iout_valley")
1018     {
1019         type = pmbus_utils::SensorValueType::iout_valley;
1020     }
1021     else if (value == "pout")
1022     {
1023         type = pmbus_utils::SensorValueType::pout;
1024     }
1025     else if (value == "temperature")
1026     {
1027         type = pmbus_utils::SensorValueType::temperature;
1028     }
1029     else if (value == "temperature_peak")
1030     {
1031         type = pmbus_utils::SensorValueType::temperature_peak;
1032     }
1033     else if (value == "vout")
1034     {
1035         type = pmbus_utils::SensorValueType::vout;
1036     }
1037     else if (value == "vout_peak")
1038     {
1039         type = pmbus_utils::SensorValueType::vout_peak;
1040     }
1041     else if (value == "vout_valley")
1042     {
1043         type = pmbus_utils::SensorValueType::vout_valley;
1044     }
1045     else
1046     {
1047         throw std::invalid_argument{"Element is not a sensor value type"};
1048     }
1049 
1050     return type;
1051 }
1052 
1053 std::unique_ptr<SetDeviceAction> parseSetDevice(const json& element)
1054 {
1055     // String deviceID
1056     std::string deviceID = parseString(element);
1057 
1058     return std::make_unique<SetDeviceAction>(deviceID);
1059 }
1060 
1061 pmbus_utils::VoutDataFormat parseVoutDataFormat(const json& element)
1062 {
1063     if (!element.is_string())
1064     {
1065         throw std::invalid_argument{"Element is not a string"};
1066     }
1067     std::string value = element.get<std::string>();
1068     pmbus_utils::VoutDataFormat format{};
1069 
1070     if (value == "linear")
1071     {
1072         format = pmbus_utils::VoutDataFormat::linear;
1073     }
1074     else if (value == "vid")
1075     {
1076         format = pmbus_utils::VoutDataFormat::vid;
1077     }
1078     else if (value == "direct")
1079     {
1080         format = pmbus_utils::VoutDataFormat::direct;
1081     }
1082     else if (value == "ieee")
1083     {
1084         format = pmbus_utils::VoutDataFormat::ieee;
1085     }
1086     else
1087     {
1088         throw std::invalid_argument{"Element is not a vout data format"};
1089     }
1090 
1091     return format;
1092 }
1093 
1094 } // namespace internal
1095 
1096 } // namespace phosphor::power::regulators::config_file_parser
1097