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_capture_bytes"))
84     {
85         action = parseI2CCaptureBytes(element["i2c_capture_bytes"]);
86         ++propertyCount;
87     }
88     else if (element.contains("i2c_compare_bit"))
89     {
90         action = parseI2CCompareBit(element["i2c_compare_bit"]);
91         ++propertyCount;
92     }
93     else if (element.contains("i2c_compare_byte"))
94     {
95         action = parseI2CCompareByte(element["i2c_compare_byte"]);
96         ++propertyCount;
97     }
98     else if (element.contains("i2c_compare_bytes"))
99     {
100         action = parseI2CCompareBytes(element["i2c_compare_bytes"]);
101         ++propertyCount;
102     }
103     else if (element.contains("i2c_write_bit"))
104     {
105         action = parseI2CWriteBit(element["i2c_write_bit"]);
106         ++propertyCount;
107     }
108     else if (element.contains("i2c_write_byte"))
109     {
110         action = parseI2CWriteByte(element["i2c_write_byte"]);
111         ++propertyCount;
112     }
113     else if (element.contains("i2c_write_bytes"))
114     {
115         action = parseI2CWriteBytes(element["i2c_write_bytes"]);
116         ++propertyCount;
117     }
118     else if (element.contains("if"))
119     {
120         action = parseIf(element["if"]);
121         ++propertyCount;
122     }
123     else if (element.contains("log_phase_fault"))
124     {
125         action = parseLogPhaseFault(element["log_phase_fault"]);
126         ++propertyCount;
127     }
128     else if (element.contains("not"))
129     {
130         action = parseNot(element["not"]);
131         ++propertyCount;
132     }
133     else if (element.contains("or"))
134     {
135         action = parseOr(element["or"]);
136         ++propertyCount;
137     }
138     else if (element.contains("pmbus_read_sensor"))
139     {
140         action = parsePMBusReadSensor(element["pmbus_read_sensor"]);
141         ++propertyCount;
142     }
143     else if (element.contains("pmbus_write_vout_command"))
144     {
145         action =
146             parsePMBusWriteVoutCommand(element["pmbus_write_vout_command"]);
147         ++propertyCount;
148     }
149     else if (element.contains("run_rule"))
150     {
151         action = parseRunRule(element["run_rule"]);
152         ++propertyCount;
153     }
154     else if (element.contains("set_device"))
155     {
156         action = parseSetDevice(element["set_device"]);
157         ++propertyCount;
158     }
159     else
160     {
161         throw std::invalid_argument{"Required action type property missing"};
162     }
163 
164     // Verify no invalid properties exist
165     verifyPropertyCount(element, propertyCount);
166 
167     return action;
168 }
169 
170 std::vector<std::unique_ptr<Action>> parseActionArray(const json& element)
171 {
172     verifyIsArray(element);
173     std::vector<std::unique_ptr<Action>> actions;
174     for (auto& actionElement : element)
175     {
176         actions.emplace_back(parseAction(actionElement));
177     }
178     return actions;
179 }
180 
181 std::unique_ptr<AndAction> parseAnd(const json& element)
182 {
183     verifyIsArray(element);
184 
185     // Verify if array size less than 2
186     if (element.size() < 2)
187     {
188         throw std::invalid_argument{"Array must contain two or more actions"};
189     }
190     // Array of two or more actions
191     std::vector<std::unique_ptr<Action>> actions = parseActionArray(element);
192 
193     return std::make_unique<AndAction>(std::move(actions));
194 }
195 
196 std::unique_ptr<Chassis> parseChassis(const json& element)
197 {
198     verifyIsObject(element);
199     unsigned int propertyCount{0};
200 
201     // Optional comments property; value not stored
202     if (element.contains("comments"))
203     {
204         ++propertyCount;
205     }
206 
207     // Required number property
208     const json& numberElement = getRequiredProperty(element, "number");
209     unsigned int number = parseUnsignedInteger(numberElement);
210     if (number < 1)
211     {
212         throw std::invalid_argument{"Invalid chassis number: Must be > 0"};
213     }
214     ++propertyCount;
215 
216     // Required inventory_path property
217     const json& inventoryPathElement =
218         getRequiredProperty(element, "inventory_path");
219     std::string inventoryPath = parseInventoryPath(inventoryPathElement);
220     ++propertyCount;
221 
222     // Optional devices property
223     std::vector<std::unique_ptr<Device>> devices{};
224     auto devicesIt = element.find("devices");
225     if (devicesIt != element.end())
226     {
227         devices = parseDeviceArray(*devicesIt);
228         ++propertyCount;
229     }
230 
231     // Verify no invalid properties exist
232     verifyPropertyCount(element, propertyCount);
233 
234     return std::make_unique<Chassis>(number, inventoryPath, std::move(devices));
235 }
236 
237 std::vector<std::unique_ptr<Chassis>> parseChassisArray(const json& element)
238 {
239     verifyIsArray(element);
240     std::vector<std::unique_ptr<Chassis>> chassis;
241     for (auto& chassisElement : element)
242     {
243         chassis.emplace_back(parseChassis(chassisElement));
244     }
245     return chassis;
246 }
247 
248 std::unique_ptr<ComparePresenceAction> parseComparePresence(const json& element)
249 {
250     verifyIsObject(element);
251     unsigned int propertyCount{0};
252 
253     // Required fru property
254     const json& fruElement = getRequiredProperty(element, "fru");
255     std::string fru = parseInventoryPath(fruElement);
256     ++propertyCount;
257 
258     // Required value property
259     const json& valueElement = getRequiredProperty(element, "value");
260     bool value = parseBoolean(valueElement);
261     ++propertyCount;
262 
263     // Verify no invalid properties exist
264     verifyPropertyCount(element, propertyCount);
265 
266     return std::make_unique<ComparePresenceAction>(fru, value);
267 }
268 
269 std::unique_ptr<CompareVPDAction> parseCompareVPD(const json& element)
270 {
271     verifyIsObject(element);
272     unsigned int propertyCount{0};
273 
274     // Required fru property
275     const json& fruElement = getRequiredProperty(element, "fru");
276     std::string fru = parseInventoryPath(fruElement);
277     ++propertyCount;
278 
279     // Required keyword property
280     const json& keywordElement = getRequiredProperty(element, "keyword");
281     std::string keyword = parseString(keywordElement);
282     ++propertyCount;
283 
284     // Either value or byte_values required property
285     auto valueIt = element.find("value");
286     std::vector<uint8_t> value{};
287     auto byteValuesIt = element.find("byte_values");
288     if ((valueIt != element.end()) && (byteValuesIt == element.end()))
289     {
290         std::string stringValue = parseString(*valueIt, true);
291         value.insert(value.begin(), stringValue.begin(), stringValue.end());
292         ++propertyCount;
293     }
294     else if ((valueIt == element.end()) && (byteValuesIt != element.end()))
295     {
296         value = parseHexByteArray(*byteValuesIt);
297         ++propertyCount;
298     }
299     else
300     {
301         throw std::invalid_argument{
302             "Invalid property: Must contain either value or byte_values"};
303     }
304 
305     // Verify no invalid properties exist
306     verifyPropertyCount(element, propertyCount);
307 
308     return std::make_unique<CompareVPDAction>(fru, keyword, value);
309 }
310 
311 std::unique_ptr<Configuration> parseConfiguration(const json& element)
312 {
313     verifyIsObject(element);
314     unsigned int propertyCount{0};
315 
316     // Optional comments property; value not stored
317     if (element.contains("comments"))
318     {
319         ++propertyCount;
320     }
321 
322     // Optional volts property
323     std::optional<double> volts{};
324     auto voltsIt = element.find("volts");
325     if (voltsIt != element.end())
326     {
327         volts = parseDouble(*voltsIt);
328         ++propertyCount;
329     }
330 
331     // Required rule_id or actions property
332     std::vector<std::unique_ptr<Action>> actions{};
333     actions = parseRuleIDOrActionsProperty(element);
334     ++propertyCount;
335 
336     // Verify no invalid properties exist
337     verifyPropertyCount(element, propertyCount);
338 
339     return std::make_unique<Configuration>(volts, std::move(actions));
340 }
341 
342 std::unique_ptr<Device> parseDevice(const json& element)
343 {
344     verifyIsObject(element);
345     unsigned int propertyCount{0};
346 
347     // Optional comments property; value not stored
348     if (element.contains("comments"))
349     {
350         ++propertyCount;
351     }
352 
353     // Required id property
354     const json& idElement = getRequiredProperty(element, "id");
355     std::string id = parseString(idElement);
356     ++propertyCount;
357 
358     // Required is_regulator property
359     const json& isRegulatorElement =
360         getRequiredProperty(element, "is_regulator");
361     bool isRegulator = parseBoolean(isRegulatorElement);
362     ++propertyCount;
363 
364     // Required fru property
365     const json& fruElement = getRequiredProperty(element, "fru");
366     std::string fru = parseInventoryPath(fruElement);
367     ++propertyCount;
368 
369     // Required i2c_interface property
370     const json& i2cInterfaceElement =
371         getRequiredProperty(element, "i2c_interface");
372     std::unique_ptr<i2c::I2CInterface> i2cInterface =
373         parseI2CInterface(i2cInterfaceElement);
374     ++propertyCount;
375 
376     // Optional presence_detection property
377     std::unique_ptr<PresenceDetection> presenceDetection{};
378     auto presenceDetectionIt = element.find("presence_detection");
379     if (presenceDetectionIt != element.end())
380     {
381         presenceDetection = parsePresenceDetection(*presenceDetectionIt);
382         ++propertyCount;
383     }
384 
385     // Optional configuration property
386     std::unique_ptr<Configuration> configuration{};
387     auto configurationIt = element.find("configuration");
388     if (configurationIt != element.end())
389     {
390         configuration = parseConfiguration(*configurationIt);
391         ++propertyCount;
392     }
393 
394     // Optional phase_fault_detection property
395     std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{};
396     auto phaseFaultDetectionIt = element.find("phase_fault_detection");
397     if (phaseFaultDetectionIt != element.end())
398     {
399         if (!isRegulator)
400         {
401             throw std::invalid_argument{"Invalid phase_fault_detection "
402                                         "property when is_regulator is false"};
403         }
404         phaseFaultDetection = parsePhaseFaultDetection(*phaseFaultDetectionIt);
405         ++propertyCount;
406     }
407 
408     // Optional rails property
409     std::vector<std::unique_ptr<Rail>> rails{};
410     auto railsIt = element.find("rails");
411     if (railsIt != element.end())
412     {
413         if (!isRegulator)
414         {
415             throw std::invalid_argument{
416                 "Invalid rails property when is_regulator is false"};
417         }
418         rails = parseRailArray(*railsIt);
419         ++propertyCount;
420     }
421 
422     // Verify no invalid properties exist
423     verifyPropertyCount(element, propertyCount);
424 
425     return std::make_unique<Device>(
426         id, isRegulator, fru, std::move(i2cInterface),
427         std::move(presenceDetection), std::move(configuration),
428         std::move(phaseFaultDetection), std::move(rails));
429 }
430 
431 std::vector<std::unique_ptr<Device>> parseDeviceArray(const json& element)
432 {
433     verifyIsArray(element);
434     std::vector<std::unique_ptr<Device>> devices;
435     for (auto& deviceElement : element)
436     {
437         devices.emplace_back(parseDevice(deviceElement));
438     }
439     return devices;
440 }
441 
442 std::vector<uint8_t> parseHexByteArray(const json& element)
443 {
444     verifyIsArray(element);
445     std::vector<uint8_t> values;
446     for (auto& valueElement : element)
447     {
448         values.emplace_back(parseHexByte(valueElement));
449     }
450     return values;
451 }
452 
453 std::unique_ptr<I2CCaptureBytesAction> parseI2CCaptureBytes(const json& element)
454 {
455     verifyIsObject(element);
456     unsigned int propertyCount{0};
457 
458     // Required register property
459     const json& regElement = getRequiredProperty(element, "register");
460     uint8_t reg = parseHexByte(regElement);
461     ++propertyCount;
462 
463     // Required count property
464     const json& countElement = getRequiredProperty(element, "count");
465     uint8_t count = parseUint8(countElement);
466     if (count < 1)
467     {
468         throw std::invalid_argument{"Invalid byte count: Must be > 0"};
469     }
470     ++propertyCount;
471 
472     // Verify no invalid properties exist
473     verifyPropertyCount(element, propertyCount);
474 
475     return std::make_unique<I2CCaptureBytesAction>(reg, count);
476 }
477 
478 std::unique_ptr<I2CCompareBitAction> parseI2CCompareBit(const json& element)
479 {
480     verifyIsObject(element);
481     unsigned int propertyCount{0};
482 
483     // Required register property
484     const json& regElement = getRequiredProperty(element, "register");
485     uint8_t reg = parseHexByte(regElement);
486     ++propertyCount;
487 
488     // Required position property
489     const json& positionElement = getRequiredProperty(element, "position");
490     uint8_t position = parseBitPosition(positionElement);
491     ++propertyCount;
492 
493     // Required value property
494     const json& valueElement = getRequiredProperty(element, "value");
495     uint8_t value = parseBitValue(valueElement);
496     ++propertyCount;
497 
498     // Verify no invalid properties exist
499     verifyPropertyCount(element, propertyCount);
500 
501     return std::make_unique<I2CCompareBitAction>(reg, position, value);
502 }
503 
504 std::unique_ptr<I2CCompareByteAction> parseI2CCompareByte(const json& element)
505 {
506     verifyIsObject(element);
507     unsigned int propertyCount{0};
508 
509     // Required register property
510     const json& regElement = getRequiredProperty(element, "register");
511     uint8_t reg = parseHexByte(regElement);
512     ++propertyCount;
513 
514     // Required value property
515     const json& valueElement = getRequiredProperty(element, "value");
516     uint8_t value = parseHexByte(valueElement);
517     ++propertyCount;
518 
519     // Optional mask property
520     uint8_t mask = 0xff;
521     auto maskIt = element.find("mask");
522     if (maskIt != element.end())
523     {
524         mask = parseHexByte(*maskIt);
525         ++propertyCount;
526     }
527 
528     // Verify no invalid properties exist
529     verifyPropertyCount(element, propertyCount);
530 
531     return std::make_unique<I2CCompareByteAction>(reg, value, mask);
532 }
533 
534 std::unique_ptr<I2CCompareBytesAction> parseI2CCompareBytes(const json& element)
535 {
536     verifyIsObject(element);
537     unsigned int propertyCount{0};
538 
539     // Required register property
540     const json& regElement = getRequiredProperty(element, "register");
541     uint8_t reg = parseHexByte(regElement);
542     ++propertyCount;
543 
544     // Required values property
545     const json& valueElement = getRequiredProperty(element, "values");
546     std::vector<uint8_t> values = parseHexByteArray(valueElement);
547     ++propertyCount;
548 
549     // Optional masks property
550     std::vector<uint8_t> masks{};
551     auto masksIt = element.find("masks");
552     if (masksIt != element.end())
553     {
554         masks = parseHexByteArray(*masksIt);
555         ++propertyCount;
556     }
557 
558     // Verify masks array (if specified) was same size as values array
559     if ((!masks.empty()) && (masks.size() != values.size()))
560     {
561         throw std::invalid_argument{"Invalid number of elements in masks"};
562     }
563 
564     // Verify no invalid properties exist
565     verifyPropertyCount(element, propertyCount);
566 
567     if (masks.empty())
568     {
569         return std::make_unique<I2CCompareBytesAction>(reg, values);
570     }
571     return std::make_unique<I2CCompareBytesAction>(reg, values, masks);
572 }
573 
574 std::unique_ptr<i2c::I2CInterface> parseI2CInterface(const json& element)
575 {
576     verifyIsObject(element);
577     unsigned int propertyCount{0};
578 
579     // Required bus property
580     const json& busElement = getRequiredProperty(element, "bus");
581     uint8_t bus = parseUint8(busElement);
582     ++propertyCount;
583 
584     // Required address property
585     const json& addressElement = getRequiredProperty(element, "address");
586     uint8_t address = parseHexByte(addressElement);
587     ++propertyCount;
588 
589     // Verify no invalid properties exist
590     verifyPropertyCount(element, propertyCount);
591 
592     // Create I2CInterface object; retry failed I2C operations a max of 3 times.
593     int maxRetries{3};
594     return i2c::create(bus, address, i2c::I2CInterface::InitialState::CLOSED,
595                        maxRetries);
596 }
597 
598 std::unique_ptr<I2CWriteBitAction> parseI2CWriteBit(const json& element)
599 {
600     verifyIsObject(element);
601     unsigned int propertyCount{0};
602 
603     // Required register property
604     const json& regElement = getRequiredProperty(element, "register");
605     uint8_t reg = parseHexByte(regElement);
606     ++propertyCount;
607 
608     // Required position property
609     const json& positionElement = getRequiredProperty(element, "position");
610     uint8_t position = parseBitPosition(positionElement);
611     ++propertyCount;
612 
613     // Required value property
614     const json& valueElement = getRequiredProperty(element, "value");
615     uint8_t value = parseBitValue(valueElement);
616     ++propertyCount;
617 
618     // Verify no invalid properties exist
619     verifyPropertyCount(element, propertyCount);
620 
621     return std::make_unique<I2CWriteBitAction>(reg, position, value);
622 }
623 
624 std::unique_ptr<I2CWriteByteAction> parseI2CWriteByte(const json& element)
625 {
626     verifyIsObject(element);
627     unsigned int propertyCount{0};
628 
629     // Required register property
630     const json& regElement = getRequiredProperty(element, "register");
631     uint8_t reg = parseHexByte(regElement);
632     ++propertyCount;
633 
634     // Required value property
635     const json& valueElement = getRequiredProperty(element, "value");
636     uint8_t value = parseHexByte(valueElement);
637     ++propertyCount;
638 
639     // Optional mask property
640     uint8_t mask = 0xff;
641     auto maskIt = element.find("mask");
642     if (maskIt != element.end())
643     {
644         mask = parseHexByte(*maskIt);
645         ++propertyCount;
646     }
647 
648     // Verify no invalid properties exist
649     verifyPropertyCount(element, propertyCount);
650 
651     return std::make_unique<I2CWriteByteAction>(reg, value, mask);
652 }
653 
654 std::unique_ptr<I2CWriteBytesAction> parseI2CWriteBytes(const json& element)
655 {
656     verifyIsObject(element);
657     unsigned int propertyCount{0};
658 
659     // Required register property
660     const json& regElement = getRequiredProperty(element, "register");
661     uint8_t reg = parseHexByte(regElement);
662     ++propertyCount;
663 
664     // Required values property
665     const json& valueElement = getRequiredProperty(element, "values");
666     std::vector<uint8_t> values = parseHexByteArray(valueElement);
667     ++propertyCount;
668 
669     // Optional masks property
670     std::vector<uint8_t> masks{};
671     auto masksIt = element.find("masks");
672     if (masksIt != element.end())
673     {
674         masks = parseHexByteArray(*masksIt);
675         ++propertyCount;
676     }
677 
678     // Verify masks array (if specified) was same size as values array
679     if ((!masks.empty()) && (masks.size() != values.size()))
680     {
681         throw std::invalid_argument{"Invalid number of elements in masks"};
682     }
683 
684     // Verify no invalid properties exist
685     verifyPropertyCount(element, propertyCount);
686 
687     if (masks.empty())
688     {
689         return std::make_unique<I2CWriteBytesAction>(reg, values);
690     }
691     return std::make_unique<I2CWriteBytesAction>(reg, values, masks);
692 }
693 
694 std::unique_ptr<IfAction> parseIf(const json& element)
695 {
696     verifyIsObject(element);
697     unsigned int propertyCount{0};
698 
699     // Required condition property
700     const json& conditionElement = getRequiredProperty(element, "condition");
701     std::unique_ptr<Action> conditionAction = parseAction(conditionElement);
702     ++propertyCount;
703 
704     // Required then property
705     const json& thenElement = getRequiredProperty(element, "then");
706     std::vector<std::unique_ptr<Action>> thenActions =
707         parseActionArray(thenElement);
708     ++propertyCount;
709 
710     // Optional else property
711     std::vector<std::unique_ptr<Action>> elseActions{};
712     auto elseIt = element.find("else");
713     if (elseIt != element.end())
714     {
715         elseActions = parseActionArray(*elseIt);
716         ++propertyCount;
717     }
718 
719     // Verify no invalid properties exist
720     verifyPropertyCount(element, propertyCount);
721 
722     return std::make_unique<IfAction>(std::move(conditionAction),
723                                       std::move(thenActions),
724                                       std::move(elseActions));
725 }
726 
727 std::string parseInventoryPath(const json& element)
728 {
729     std::string inventoryPath = parseString(element);
730     std::string absPath = "/xyz/openbmc_project/inventory";
731     if (inventoryPath.front() != '/')
732     {
733         absPath += '/';
734     }
735     absPath += inventoryPath;
736     return absPath;
737 }
738 
739 std::unique_ptr<LogPhaseFaultAction> parseLogPhaseFault(const json& element)
740 {
741     verifyIsObject(element);
742     unsigned int propertyCount{0};
743 
744     // Required type property
745     const json& typeElement = getRequiredProperty(element, "type");
746     PhaseFaultType type = parsePhaseFaultType(typeElement);
747     ++propertyCount;
748 
749     // Verify no invalid properties exist
750     verifyPropertyCount(element, propertyCount);
751 
752     return std::make_unique<LogPhaseFaultAction>(type);
753 }
754 
755 std::unique_ptr<NotAction> parseNot(const json& element)
756 {
757     // Required action to execute
758     std::unique_ptr<Action> action = parseAction(element);
759 
760     return std::make_unique<NotAction>(std::move(action));
761 }
762 
763 std::unique_ptr<OrAction> parseOr(const json& element)
764 {
765     verifyIsArray(element);
766 
767     // Verify if array size less than 2
768     if (element.size() < 2)
769     {
770         throw std::invalid_argument{"Array must contain two or more actions"};
771     }
772     // Array of two or more actions
773     std::vector<std::unique_ptr<Action>> actions = parseActionArray(element);
774 
775     return std::make_unique<OrAction>(std::move(actions));
776 }
777 
778 std::unique_ptr<PhaseFaultDetection>
779     parsePhaseFaultDetection(const json& element)
780 {
781     verifyIsObject(element);
782     unsigned int propertyCount{0};
783 
784     // Optional comments property; value not stored
785     if (element.contains("comments"))
786     {
787         ++propertyCount;
788     }
789 
790     // Optional device_id property
791     std::string deviceID{};
792     auto deviceIDIt = element.find("device_id");
793     if (deviceIDIt != element.end())
794     {
795         deviceID = parseString(*deviceIDIt);
796         ++propertyCount;
797     }
798 
799     // Required rule_id or actions property
800     std::vector<std::unique_ptr<Action>> actions{};
801     actions = parseRuleIDOrActionsProperty(element);
802     ++propertyCount;
803 
804     // Verify no invalid properties exist
805     verifyPropertyCount(element, propertyCount);
806 
807     return std::make_unique<PhaseFaultDetection>(std::move(actions), deviceID);
808 }
809 
810 PhaseFaultType parsePhaseFaultType(const json& element)
811 {
812     std::string value = parseString(element);
813     PhaseFaultType type{};
814 
815     if (value == "n")
816     {
817         type = PhaseFaultType::n;
818     }
819     else if (value == "n+1")
820     {
821         type = PhaseFaultType::n_plus_1;
822     }
823     else
824     {
825         throw std::invalid_argument{"Element is not a phase fault type"};
826     }
827 
828     return type;
829 }
830 
831 std::unique_ptr<PMBusReadSensorAction> parsePMBusReadSensor(const json& element)
832 {
833     verifyIsObject(element);
834     unsigned int propertyCount{0};
835 
836     // Required type property
837     const json& typeElement = getRequiredProperty(element, "type");
838     SensorType type = parseSensorType(typeElement);
839     ++propertyCount;
840 
841     // Required command property
842     const json& commandElement = getRequiredProperty(element, "command");
843     uint8_t command = parseHexByte(commandElement);
844     ++propertyCount;
845 
846     // Required format property
847     const json& formatElement = getRequiredProperty(element, "format");
848     pmbus_utils::SensorDataFormat format = parseSensorDataFormat(formatElement);
849     ++propertyCount;
850 
851     // Optional exponent property
852     std::optional<int8_t> exponent{};
853     auto exponentIt = element.find("exponent");
854     if (exponentIt != element.end())
855     {
856         exponent = parseInt8(*exponentIt);
857         ++propertyCount;
858     }
859 
860     // Verify no invalid properties exist
861     verifyPropertyCount(element, propertyCount);
862 
863     return std::make_unique<PMBusReadSensorAction>(type, command, format,
864                                                    exponent);
865 }
866 
867 std::unique_ptr<PMBusWriteVoutCommandAction>
868     parsePMBusWriteVoutCommand(const json& element)
869 {
870     verifyIsObject(element);
871     unsigned int propertyCount{0};
872 
873     // Optional volts property
874     std::optional<double> volts{};
875     auto voltsIt = element.find("volts");
876     if (voltsIt != element.end())
877     {
878         volts = parseDouble(*voltsIt);
879         ++propertyCount;
880     }
881 
882     // Required format property
883     const json& formatElement = getRequiredProperty(element, "format");
884     std::string formatString = parseString(formatElement);
885     if (formatString != "linear")
886     {
887         throw std::invalid_argument{"Invalid format value: " + formatString};
888     }
889     pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::linear;
890     ++propertyCount;
891 
892     // Optional exponent property
893     std::optional<int8_t> exponent{};
894     auto exponentIt = element.find("exponent");
895     if (exponentIt != element.end())
896     {
897         exponent = parseInt8(*exponentIt);
898         ++propertyCount;
899     }
900 
901     // Optional is_verified property
902     bool isVerified = false;
903     auto isVerifiedIt = element.find("is_verified");
904     if (isVerifiedIt != element.end())
905     {
906         isVerified = parseBoolean(*isVerifiedIt);
907         ++propertyCount;
908     }
909 
910     // Verify no invalid properties exist
911     verifyPropertyCount(element, propertyCount);
912 
913     return std::make_unique<PMBusWriteVoutCommandAction>(volts, format,
914                                                          exponent, isVerified);
915 }
916 
917 std::unique_ptr<PresenceDetection> parsePresenceDetection(const json& element)
918 {
919     verifyIsObject(element);
920     unsigned int propertyCount{0};
921 
922     // Optional comments property; value not stored
923     if (element.contains("comments"))
924     {
925         ++propertyCount;
926     }
927 
928     // Required rule_id or actions property
929     std::vector<std::unique_ptr<Action>> actions{};
930     actions = parseRuleIDOrActionsProperty(element);
931     ++propertyCount;
932 
933     // Verify no invalid properties exist
934     verifyPropertyCount(element, propertyCount);
935 
936     return std::make_unique<PresenceDetection>(std::move(actions));
937 }
938 
939 std::unique_ptr<Rail> parseRail(const json& element)
940 {
941     verifyIsObject(element);
942     unsigned int propertyCount{0};
943 
944     // Optional comments property; value not stored
945     if (element.contains("comments"))
946     {
947         ++propertyCount;
948     }
949 
950     // Required id property
951     const json& idElement = getRequiredProperty(element, "id");
952     std::string id = parseString(idElement);
953     ++propertyCount;
954 
955     // Optional configuration property
956     std::unique_ptr<Configuration> configuration{};
957     auto configurationIt = element.find("configuration");
958     if (configurationIt != element.end())
959     {
960         configuration = parseConfiguration(*configurationIt);
961         ++propertyCount;
962     }
963 
964     // Optional sensor_monitoring property
965     std::unique_ptr<SensorMonitoring> sensorMonitoring{};
966     auto sensorMonitoringIt = element.find("sensor_monitoring");
967     if (sensorMonitoringIt != element.end())
968     {
969         sensorMonitoring = parseSensorMonitoring(*sensorMonitoringIt);
970         ++propertyCount;
971     }
972 
973     // Verify no invalid properties exist
974     verifyPropertyCount(element, propertyCount);
975 
976     return std::make_unique<Rail>(id, std::move(configuration),
977                                   std::move(sensorMonitoring));
978 }
979 
980 std::vector<std::unique_ptr<Rail>> parseRailArray(const json& element)
981 {
982     verifyIsArray(element);
983     std::vector<std::unique_ptr<Rail>> rails;
984     for (auto& railElement : element)
985     {
986         rails.emplace_back(parseRail(railElement));
987     }
988     return rails;
989 }
990 
991 std::tuple<std::vector<std::unique_ptr<Rule>>,
992            std::vector<std::unique_ptr<Chassis>>>
993     parseRoot(const json& element)
994 {
995     verifyIsObject(element);
996     unsigned int propertyCount{0};
997 
998     // Optional comments property; value not stored
999     if (element.contains("comments"))
1000     {
1001         ++propertyCount;
1002     }
1003 
1004     // Optional rules property
1005     std::vector<std::unique_ptr<Rule>> rules{};
1006     auto rulesIt = element.find("rules");
1007     if (rulesIt != element.end())
1008     {
1009         rules = parseRuleArray(*rulesIt);
1010         ++propertyCount;
1011     }
1012 
1013     // Required chassis property
1014     const json& chassisElement = getRequiredProperty(element, "chassis");
1015     std::vector<std::unique_ptr<Chassis>> chassis =
1016         parseChassisArray(chassisElement);
1017     ++propertyCount;
1018 
1019     // Verify no invalid properties exist
1020     verifyPropertyCount(element, propertyCount);
1021 
1022     return std::make_tuple(std::move(rules), std::move(chassis));
1023 }
1024 
1025 std::unique_ptr<Rule> parseRule(const json& element)
1026 {
1027     verifyIsObject(element);
1028     unsigned int propertyCount{0};
1029 
1030     // Optional comments property; value not stored
1031     if (element.contains("comments"))
1032     {
1033         ++propertyCount;
1034     }
1035 
1036     // Required id property
1037     const json& idElement = getRequiredProperty(element, "id");
1038     std::string id = parseString(idElement);
1039     ++propertyCount;
1040 
1041     // Required actions property
1042     const json& actionsElement = getRequiredProperty(element, "actions");
1043     std::vector<std::unique_ptr<Action>> actions =
1044         parseActionArray(actionsElement);
1045     ++propertyCount;
1046 
1047     // Verify no invalid properties exist
1048     verifyPropertyCount(element, propertyCount);
1049 
1050     return std::make_unique<Rule>(id, std::move(actions));
1051 }
1052 
1053 std::vector<std::unique_ptr<Rule>> parseRuleArray(const json& element)
1054 {
1055     verifyIsArray(element);
1056     std::vector<std::unique_ptr<Rule>> rules;
1057     for (auto& ruleElement : element)
1058     {
1059         rules.emplace_back(parseRule(ruleElement));
1060     }
1061     return rules;
1062 }
1063 
1064 std::vector<std::unique_ptr<Action>>
1065     parseRuleIDOrActionsProperty(const json& element)
1066 {
1067     verifyIsObject(element);
1068     // Required rule_id or actions property
1069     std::vector<std::unique_ptr<Action>> actions{};
1070     auto ruleIDIt = element.find("rule_id");
1071     auto actionsIt = element.find("actions");
1072     if ((actionsIt == element.end()) && (ruleIDIt != element.end()))
1073     {
1074         std::string ruleID = parseString(*ruleIDIt);
1075         actions.emplace_back(std::make_unique<RunRuleAction>(ruleID));
1076     }
1077     else if ((actionsIt != element.end()) && (ruleIDIt == element.end()))
1078     {
1079         actions = parseActionArray(*actionsIt);
1080     }
1081     else
1082     {
1083         throw std::invalid_argument{"Invalid property combination: Must "
1084                                     "contain either rule_id or actions"};
1085     }
1086 
1087     return actions;
1088 }
1089 
1090 std::unique_ptr<RunRuleAction> parseRunRule(const json& element)
1091 {
1092     // String ruleID
1093     std::string ruleID = parseString(element);
1094 
1095     return std::make_unique<RunRuleAction>(ruleID);
1096 }
1097 
1098 pmbus_utils::SensorDataFormat parseSensorDataFormat(const json& element)
1099 {
1100     if (!element.is_string())
1101     {
1102         throw std::invalid_argument{"Element is not a string"};
1103     }
1104     std::string value = element.get<std::string>();
1105     pmbus_utils::SensorDataFormat format{};
1106 
1107     if (value == "linear_11")
1108     {
1109         format = pmbus_utils::SensorDataFormat::linear_11;
1110     }
1111     else if (value == "linear_16")
1112     {
1113         format = pmbus_utils::SensorDataFormat::linear_16;
1114     }
1115     else
1116     {
1117         throw std::invalid_argument{"Element is not a sensor data format"};
1118     }
1119 
1120     return format;
1121 }
1122 
1123 std::unique_ptr<SensorMonitoring> parseSensorMonitoring(const json& element)
1124 {
1125     verifyIsObject(element);
1126     unsigned int propertyCount{0};
1127 
1128     // Optional comments property; value not stored
1129     if (element.contains("comments"))
1130     {
1131         ++propertyCount;
1132     }
1133 
1134     // Required rule_id or actions property
1135     std::vector<std::unique_ptr<Action>> actions{};
1136     actions = parseRuleIDOrActionsProperty(element);
1137     ++propertyCount;
1138 
1139     // Verify no invalid properties exist
1140     verifyPropertyCount(element, propertyCount);
1141 
1142     return std::make_unique<SensorMonitoring>(std::move(actions));
1143 }
1144 
1145 SensorType parseSensorType(const json& element)
1146 {
1147     std::string value = parseString(element);
1148     SensorType type{};
1149 
1150     if (value == "iout")
1151     {
1152         type = SensorType::iout;
1153     }
1154     else if (value == "iout_peak")
1155     {
1156         type = SensorType::iout_peak;
1157     }
1158     else if (value == "iout_valley")
1159     {
1160         type = SensorType::iout_valley;
1161     }
1162     else if (value == "pout")
1163     {
1164         type = SensorType::pout;
1165     }
1166     else if (value == "temperature")
1167     {
1168         type = SensorType::temperature;
1169     }
1170     else if (value == "temperature_peak")
1171     {
1172         type = SensorType::temperature_peak;
1173     }
1174     else if (value == "vout")
1175     {
1176         type = SensorType::vout;
1177     }
1178     else if (value == "vout_peak")
1179     {
1180         type = SensorType::vout_peak;
1181     }
1182     else if (value == "vout_valley")
1183     {
1184         type = SensorType::vout_valley;
1185     }
1186     else
1187     {
1188         throw std::invalid_argument{"Element is not a sensor type"};
1189     }
1190 
1191     return type;
1192 }
1193 
1194 std::unique_ptr<SetDeviceAction> parseSetDevice(const json& element)
1195 {
1196     // String deviceID
1197     std::string deviceID = parseString(element);
1198 
1199     return std::make_unique<SetDeviceAction>(deviceID);
1200 }
1201 
1202 pmbus_utils::VoutDataFormat parseVoutDataFormat(const json& element)
1203 {
1204     if (!element.is_string())
1205     {
1206         throw std::invalid_argument{"Element is not a string"};
1207     }
1208     std::string value = element.get<std::string>();
1209     pmbus_utils::VoutDataFormat format{};
1210 
1211     if (value == "linear")
1212     {
1213         format = pmbus_utils::VoutDataFormat::linear;
1214     }
1215     else if (value == "vid")
1216     {
1217         format = pmbus_utils::VoutDataFormat::vid;
1218     }
1219     else if (value == "direct")
1220     {
1221         format = pmbus_utils::VoutDataFormat::direct;
1222     }
1223     else if (value == "ieee")
1224     {
1225         format = pmbus_utils::VoutDataFormat::ieee;
1226     }
1227     else
1228     {
1229         throw std::invalid_argument{"Element is not a vout data format"};
1230     }
1231 
1232     return format;
1233 }
1234 
1235 } // namespace internal
1236 
1237 } // namespace phosphor::power::regulators::config_file_parser
1238