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 inventory_path property.  Will be required in future.
207     std::string inventoryPath{"/xyz/openbmc_project/inventory/system/chassis"};
208     auto inventoryPathIt = element.find("inventory_path");
209     if (inventoryPathIt != element.end())
210     {
211         inventoryPath = parseInventoryPath(*inventoryPathIt);
212         ++propertyCount;
213     }
214 
215     // Optional devices property
216     std::vector<std::unique_ptr<Device>> devices{};
217     auto devicesIt = element.find("devices");
218     if (devicesIt != element.end())
219     {
220         devices = parseDeviceArray(*devicesIt);
221         ++propertyCount;
222     }
223 
224     // Verify no invalid properties exist
225     verifyPropertyCount(element, propertyCount);
226 
227     return std::make_unique<Chassis>(number, inventoryPath, std::move(devices));
228 }
229 
230 std::vector<std::unique_ptr<Chassis>> parseChassisArray(const json& element)
231 {
232     verifyIsArray(element);
233     std::vector<std::unique_ptr<Chassis>> chassis;
234     for (auto& chassisElement : element)
235     {
236         chassis.emplace_back(parseChassis(chassisElement));
237     }
238     return chassis;
239 }
240 
241 std::unique_ptr<ComparePresenceAction> parseComparePresence(const json& element)
242 {
243     verifyIsObject(element);
244     unsigned int propertyCount{0};
245 
246     // Required fru property
247     const json& fruElement = getRequiredProperty(element, "fru");
248     std::string fru = parseInventoryPath(fruElement);
249     ++propertyCount;
250 
251     // Required value property
252     const json& valueElement = getRequiredProperty(element, "value");
253     bool value = parseBoolean(valueElement);
254     ++propertyCount;
255 
256     // Verify no invalid properties exist
257     verifyPropertyCount(element, propertyCount);
258 
259     return std::make_unique<ComparePresenceAction>(fru, value);
260 }
261 
262 std::unique_ptr<CompareVPDAction> parseCompareVPD(const json& element)
263 {
264     verifyIsObject(element);
265     unsigned int propertyCount{0};
266 
267     // Required fru property
268     const json& fruElement = getRequiredProperty(element, "fru");
269     std::string fru = parseInventoryPath(fruElement);
270     ++propertyCount;
271 
272     // Required keyword property
273     const json& keywordElement = getRequiredProperty(element, "keyword");
274     std::string keyword = parseString(keywordElement);
275     ++propertyCount;
276 
277     // Required value property
278     const json& valueElement = getRequiredProperty(element, "value");
279     std::string value = parseString(valueElement);
280     ++propertyCount;
281 
282     // Verify no invalid properties exist
283     verifyPropertyCount(element, propertyCount);
284 
285     return std::make_unique<CompareVPDAction>(fru, keyword, value);
286 }
287 
288 std::unique_ptr<Configuration> parseConfiguration(const json& element)
289 {
290     verifyIsObject(element);
291     unsigned int propertyCount{0};
292 
293     // Optional comments property; value not stored
294     if (element.contains("comments"))
295     {
296         ++propertyCount;
297     }
298 
299     // Optional volts property
300     std::optional<double> volts{};
301     auto voltsIt = element.find("volts");
302     if (voltsIt != element.end())
303     {
304         volts = parseDouble(*voltsIt);
305         ++propertyCount;
306     }
307 
308     // Required rule_id or actions property
309     std::vector<std::unique_ptr<Action>> actions{};
310     actions = parseRuleIDOrActionsProperty(element);
311     ++propertyCount;
312 
313     // Verify no invalid properties exist
314     verifyPropertyCount(element, propertyCount);
315 
316     return std::make_unique<Configuration>(volts, std::move(actions));
317 }
318 
319 std::unique_ptr<Device> parseDevice(const json& element)
320 {
321     verifyIsObject(element);
322     unsigned int propertyCount{0};
323 
324     // Optional comments property; value not stored
325     if (element.contains("comments"))
326     {
327         ++propertyCount;
328     }
329 
330     // Required id property
331     const json& idElement = getRequiredProperty(element, "id");
332     std::string id = parseString(idElement);
333     ++propertyCount;
334 
335     // Required is_regulator property
336     const json& isRegulatorElement =
337         getRequiredProperty(element, "is_regulator");
338     bool isRegulator = parseBoolean(isRegulatorElement);
339     ++propertyCount;
340 
341     // Required fru property
342     const json& fruElement = getRequiredProperty(element, "fru");
343     std::string fru = parseInventoryPath(fruElement);
344     ++propertyCount;
345 
346     // Required i2c_interface property
347     const json& i2cInterfaceElement =
348         getRequiredProperty(element, "i2c_interface");
349     std::unique_ptr<i2c::I2CInterface> i2cInterface =
350         parseI2CInterface(i2cInterfaceElement);
351     ++propertyCount;
352 
353     // Optional presence_detection property
354     std::unique_ptr<PresenceDetection> presenceDetection{};
355     auto presenceDetectionIt = element.find("presence_detection");
356     if (presenceDetectionIt != element.end())
357     {
358         presenceDetection = parsePresenceDetection(*presenceDetectionIt);
359         ++propertyCount;
360     }
361 
362     // Optional configuration property
363     std::unique_ptr<Configuration> configuration{};
364     auto configurationIt = element.find("configuration");
365     if (configurationIt != element.end())
366     {
367         configuration = parseConfiguration(*configurationIt);
368         ++propertyCount;
369     }
370 
371     // Optional rails property
372     std::vector<std::unique_ptr<Rail>> rails{};
373     auto railsIt = element.find("rails");
374     if (railsIt != element.end())
375     {
376         if (!isRegulator)
377         {
378             throw std::invalid_argument{
379                 "Invalid rails property when is_regulator is false"};
380         }
381         rails = parseRailArray(*railsIt);
382         ++propertyCount;
383     }
384 
385     // Verify no invalid properties exist
386     verifyPropertyCount(element, propertyCount);
387 
388     return std::make_unique<Device>(id, isRegulator, fru,
389                                     std::move(i2cInterface),
390                                     std::move(presenceDetection),
391                                     std::move(configuration), std::move(rails));
392 }
393 
394 std::vector<std::unique_ptr<Device>> parseDeviceArray(const json& element)
395 {
396     verifyIsArray(element);
397     std::vector<std::unique_ptr<Device>> devices;
398     for (auto& deviceElement : element)
399     {
400         devices.emplace_back(parseDevice(deviceElement));
401     }
402     return devices;
403 }
404 
405 std::vector<uint8_t> parseHexByteArray(const json& element)
406 {
407     verifyIsArray(element);
408     std::vector<uint8_t> values;
409     for (auto& valueElement : element)
410     {
411         values.emplace_back(parseHexByte(valueElement));
412     }
413     return values;
414 }
415 
416 std::unique_ptr<I2CCompareBitAction> parseI2CCompareBit(const json& element)
417 {
418     verifyIsObject(element);
419     unsigned int propertyCount{0};
420 
421     // Required register property
422     const json& regElement = getRequiredProperty(element, "register");
423     uint8_t reg = parseHexByte(regElement);
424     ++propertyCount;
425 
426     // Required position property
427     const json& positionElement = getRequiredProperty(element, "position");
428     uint8_t position = parseBitPosition(positionElement);
429     ++propertyCount;
430 
431     // Required value property
432     const json& valueElement = getRequiredProperty(element, "value");
433     uint8_t value = parseBitValue(valueElement);
434     ++propertyCount;
435 
436     // Verify no invalid properties exist
437     verifyPropertyCount(element, propertyCount);
438 
439     return std::make_unique<I2CCompareBitAction>(reg, position, value);
440 }
441 
442 std::unique_ptr<I2CCompareByteAction> parseI2CCompareByte(const json& element)
443 {
444     verifyIsObject(element);
445     unsigned int propertyCount{0};
446 
447     // Required register property
448     const json& regElement = getRequiredProperty(element, "register");
449     uint8_t reg = parseHexByte(regElement);
450     ++propertyCount;
451 
452     // Required value property
453     const json& valueElement = getRequiredProperty(element, "value");
454     uint8_t value = parseHexByte(valueElement);
455     ++propertyCount;
456 
457     // Optional mask property
458     uint8_t mask = 0xff;
459     auto maskIt = element.find("mask");
460     if (maskIt != element.end())
461     {
462         mask = parseHexByte(*maskIt);
463         ++propertyCount;
464     }
465 
466     // Verify no invalid properties exist
467     verifyPropertyCount(element, propertyCount);
468 
469     return std::make_unique<I2CCompareByteAction>(reg, value, mask);
470 }
471 
472 std::unique_ptr<I2CCompareBytesAction> parseI2CCompareBytes(const json& element)
473 {
474     verifyIsObject(element);
475     unsigned int propertyCount{0};
476 
477     // Required register property
478     const json& regElement = getRequiredProperty(element, "register");
479     uint8_t reg = parseHexByte(regElement);
480     ++propertyCount;
481 
482     // Required values property
483     const json& valueElement = getRequiredProperty(element, "values");
484     std::vector<uint8_t> values = parseHexByteArray(valueElement);
485     ++propertyCount;
486 
487     // Optional masks property
488     std::vector<uint8_t> masks{};
489     auto masksIt = element.find("masks");
490     if (masksIt != element.end())
491     {
492         masks = parseHexByteArray(*masksIt);
493         ++propertyCount;
494     }
495 
496     // Verify masks array (if specified) was same size as values array
497     if ((!masks.empty()) && (masks.size() != values.size()))
498     {
499         throw std::invalid_argument{"Invalid number of elements in masks"};
500     }
501 
502     // Verify no invalid properties exist
503     verifyPropertyCount(element, propertyCount);
504 
505     if (masks.empty())
506     {
507         return std::make_unique<I2CCompareBytesAction>(reg, values);
508     }
509     return std::make_unique<I2CCompareBytesAction>(reg, values, masks);
510 }
511 
512 std::unique_ptr<i2c::I2CInterface> parseI2CInterface(const json& element)
513 {
514     verifyIsObject(element);
515     unsigned int propertyCount{0};
516 
517     // Required bus property
518     const json& busElement = getRequiredProperty(element, "bus");
519     uint8_t bus = parseUint8(busElement);
520     ++propertyCount;
521 
522     // Required address property
523     const json& addressElement = getRequiredProperty(element, "address");
524     uint8_t address = parseHexByte(addressElement);
525     ++propertyCount;
526 
527     verifyPropertyCount(element, propertyCount);
528     return i2c::create(bus, address, i2c::I2CInterface::InitialState::CLOSED);
529 }
530 
531 std::unique_ptr<I2CWriteBitAction> parseI2CWriteBit(const json& element)
532 {
533     verifyIsObject(element);
534     unsigned int propertyCount{0};
535 
536     // Required register property
537     const json& regElement = getRequiredProperty(element, "register");
538     uint8_t reg = parseHexByte(regElement);
539     ++propertyCount;
540 
541     // Required position property
542     const json& positionElement = getRequiredProperty(element, "position");
543     uint8_t position = parseBitPosition(positionElement);
544     ++propertyCount;
545 
546     // Required value property
547     const json& valueElement = getRequiredProperty(element, "value");
548     uint8_t value = parseBitValue(valueElement);
549     ++propertyCount;
550 
551     // Verify no invalid properties exist
552     verifyPropertyCount(element, propertyCount);
553 
554     return std::make_unique<I2CWriteBitAction>(reg, position, value);
555 }
556 
557 std::unique_ptr<I2CWriteByteAction> parseI2CWriteByte(const json& element)
558 {
559     verifyIsObject(element);
560     unsigned int propertyCount{0};
561 
562     // Required register property
563     const json& regElement = getRequiredProperty(element, "register");
564     uint8_t reg = parseHexByte(regElement);
565     ++propertyCount;
566 
567     // Required value property
568     const json& valueElement = getRequiredProperty(element, "value");
569     uint8_t value = parseHexByte(valueElement);
570     ++propertyCount;
571 
572     // Optional mask property
573     uint8_t mask = 0xff;
574     auto maskIt = element.find("mask");
575     if (maskIt != element.end())
576     {
577         mask = parseHexByte(*maskIt);
578         ++propertyCount;
579     }
580 
581     // Verify no invalid properties exist
582     verifyPropertyCount(element, propertyCount);
583 
584     return std::make_unique<I2CWriteByteAction>(reg, value, mask);
585 }
586 
587 std::unique_ptr<I2CWriteBytesAction> parseI2CWriteBytes(const json& element)
588 {
589     verifyIsObject(element);
590     unsigned int propertyCount{0};
591 
592     // Required register property
593     const json& regElement = getRequiredProperty(element, "register");
594     uint8_t reg = parseHexByte(regElement);
595     ++propertyCount;
596 
597     // Required values property
598     const json& valueElement = getRequiredProperty(element, "values");
599     std::vector<uint8_t> values = parseHexByteArray(valueElement);
600     ++propertyCount;
601 
602     // Optional masks property
603     std::vector<uint8_t> masks{};
604     auto masksIt = element.find("masks");
605     if (masksIt != element.end())
606     {
607         masks = parseHexByteArray(*masksIt);
608         ++propertyCount;
609     }
610 
611     // Verify masks array (if specified) was same size as values array
612     if ((!masks.empty()) && (masks.size() != values.size()))
613     {
614         throw std::invalid_argument{"Invalid number of elements in masks"};
615     }
616 
617     // Verify no invalid properties exist
618     verifyPropertyCount(element, propertyCount);
619 
620     if (masks.empty())
621     {
622         return std::make_unique<I2CWriteBytesAction>(reg, values);
623     }
624     return std::make_unique<I2CWriteBytesAction>(reg, values, masks);
625 }
626 
627 std::unique_ptr<IfAction> parseIf(const json& element)
628 {
629     verifyIsObject(element);
630     unsigned int propertyCount{0};
631 
632     // Required condition property
633     const json& conditionElement = getRequiredProperty(element, "condition");
634     std::unique_ptr<Action> conditionAction = parseAction(conditionElement);
635     ++propertyCount;
636 
637     // Required then property
638     const json& thenElement = getRequiredProperty(element, "then");
639     std::vector<std::unique_ptr<Action>> thenActions =
640         parseActionArray(thenElement);
641     ++propertyCount;
642 
643     // Optional else property
644     std::vector<std::unique_ptr<Action>> elseActions{};
645     auto elseIt = element.find("else");
646     if (elseIt != element.end())
647     {
648         elseActions = parseActionArray(*elseIt);
649         ++propertyCount;
650     }
651 
652     // Verify no invalid properties exist
653     verifyPropertyCount(element, propertyCount);
654 
655     return std::make_unique<IfAction>(std::move(conditionAction),
656                                       std::move(thenActions),
657                                       std::move(elseActions));
658 }
659 
660 std::string parseInventoryPath(const json& element)
661 {
662     std::string inventoryPath = parseString(element);
663     std::string absPath = "/xyz/openbmc_project/inventory";
664     if (inventoryPath.front() != '/')
665     {
666         absPath += '/';
667     }
668     absPath += inventoryPath;
669     return absPath;
670 }
671 
672 std::unique_ptr<NotAction> parseNot(const json& element)
673 {
674     // Required action to execute
675     std::unique_ptr<Action> action = parseAction(element);
676 
677     return std::make_unique<NotAction>(std::move(action));
678 }
679 
680 std::unique_ptr<OrAction> parseOr(const json& element)
681 {
682     verifyIsArray(element);
683 
684     // Verify if array size less than 2
685     if (element.size() < 2)
686     {
687         throw std::invalid_argument{"Array must contain two or more actions"};
688     }
689     // Array of two or more actions
690     std::vector<std::unique_ptr<Action>> actions = parseActionArray(element);
691 
692     return std::make_unique<OrAction>(std::move(actions));
693 }
694 
695 std::unique_ptr<PMBusReadSensorAction> parsePMBusReadSensor(const json& element)
696 {
697     verifyIsObject(element);
698     unsigned int propertyCount{0};
699 
700     // Required type property
701     const json& typeElement = getRequiredProperty(element, "type");
702     SensorType type = parseSensorType(typeElement);
703     ++propertyCount;
704 
705     // Required command property
706     const json& commandElement = getRequiredProperty(element, "command");
707     uint8_t command = parseHexByte(commandElement);
708     ++propertyCount;
709 
710     // Required format property
711     const json& formatElement = getRequiredProperty(element, "format");
712     pmbus_utils::SensorDataFormat format = parseSensorDataFormat(formatElement);
713     ++propertyCount;
714 
715     // Optional exponent property
716     std::optional<int8_t> exponent{};
717     auto exponentIt = element.find("exponent");
718     if (exponentIt != element.end())
719     {
720         exponent = parseInt8(*exponentIt);
721         ++propertyCount;
722     }
723 
724     // Verify no invalid properties exist
725     verifyPropertyCount(element, propertyCount);
726 
727     return std::make_unique<PMBusReadSensorAction>(type, command, format,
728                                                    exponent);
729 }
730 
731 std::unique_ptr<PMBusWriteVoutCommandAction>
732     parsePMBusWriteVoutCommand(const json& element)
733 {
734     verifyIsObject(element);
735     unsigned int propertyCount{0};
736 
737     // Optional volts property
738     std::optional<double> volts{};
739     auto voltsIt = element.find("volts");
740     if (voltsIt != element.end())
741     {
742         volts = parseDouble(*voltsIt);
743         ++propertyCount;
744     }
745 
746     // Required format property
747     const json& formatElement = getRequiredProperty(element, "format");
748     std::string formatString = parseString(formatElement);
749     if (formatString != "linear")
750     {
751         throw std::invalid_argument{"Invalid format value: " + formatString};
752     }
753     pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::linear;
754     ++propertyCount;
755 
756     // Optional exponent property
757     std::optional<int8_t> exponent{};
758     auto exponentIt = element.find("exponent");
759     if (exponentIt != element.end())
760     {
761         exponent = parseInt8(*exponentIt);
762         ++propertyCount;
763     }
764 
765     // Optional is_verified property
766     bool isVerified = false;
767     auto isVerifiedIt = element.find("is_verified");
768     if (isVerifiedIt != element.end())
769     {
770         isVerified = parseBoolean(*isVerifiedIt);
771         ++propertyCount;
772     }
773 
774     // Verify no invalid properties exist
775     verifyPropertyCount(element, propertyCount);
776 
777     return std::make_unique<PMBusWriteVoutCommandAction>(volts, format,
778                                                          exponent, isVerified);
779 }
780 
781 std::unique_ptr<PresenceDetection> parsePresenceDetection(const json& element)
782 {
783     verifyIsObject(element);
784     unsigned int propertyCount{0};
785 
786     // Optional comments property; value not stored
787     if (element.contains("comments"))
788     {
789         ++propertyCount;
790     }
791 
792     // Required rule_id or actions property
793     std::vector<std::unique_ptr<Action>> actions{};
794     actions = parseRuleIDOrActionsProperty(element);
795     ++propertyCount;
796 
797     // Verify no invalid properties exist
798     verifyPropertyCount(element, propertyCount);
799 
800     return std::make_unique<PresenceDetection>(std::move(actions));
801 }
802 
803 std::unique_ptr<Rail> parseRail(const json& element)
804 {
805     verifyIsObject(element);
806     unsigned int propertyCount{0};
807 
808     // Optional comments property; value not stored
809     if (element.contains("comments"))
810     {
811         ++propertyCount;
812     }
813 
814     // Required id property
815     const json& idElement = getRequiredProperty(element, "id");
816     std::string id = parseString(idElement);
817     ++propertyCount;
818 
819     // Optional configuration property
820     std::unique_ptr<Configuration> configuration{};
821     auto configurationIt = element.find("configuration");
822     if (configurationIt != element.end())
823     {
824         configuration = parseConfiguration(*configurationIt);
825         ++propertyCount;
826     }
827 
828     // Optional sensor_monitoring property
829     std::unique_ptr<SensorMonitoring> sensorMonitoring{};
830     auto sensorMonitoringIt = element.find("sensor_monitoring");
831     if (sensorMonitoringIt != element.end())
832     {
833         sensorMonitoring = parseSensorMonitoring(*sensorMonitoringIt);
834         ++propertyCount;
835     }
836 
837     // Verify no invalid properties exist
838     verifyPropertyCount(element, propertyCount);
839 
840     return std::make_unique<Rail>(id, std::move(configuration),
841                                   std::move(sensorMonitoring));
842 }
843 
844 std::vector<std::unique_ptr<Rail>> parseRailArray(const json& element)
845 {
846     verifyIsArray(element);
847     std::vector<std::unique_ptr<Rail>> rails;
848     for (auto& railElement : element)
849     {
850         rails.emplace_back(parseRail(railElement));
851     }
852     return rails;
853 }
854 
855 std::tuple<std::vector<std::unique_ptr<Rule>>,
856            std::vector<std::unique_ptr<Chassis>>>
857     parseRoot(const json& element)
858 {
859     verifyIsObject(element);
860     unsigned int propertyCount{0};
861 
862     // Optional comments property; value not stored
863     if (element.contains("comments"))
864     {
865         ++propertyCount;
866     }
867 
868     // Optional rules property
869     std::vector<std::unique_ptr<Rule>> rules{};
870     auto rulesIt = element.find("rules");
871     if (rulesIt != element.end())
872     {
873         rules = parseRuleArray(*rulesIt);
874         ++propertyCount;
875     }
876 
877     // Required chassis property
878     const json& chassisElement = getRequiredProperty(element, "chassis");
879     std::vector<std::unique_ptr<Chassis>> chassis =
880         parseChassisArray(chassisElement);
881     ++propertyCount;
882 
883     // Verify no invalid properties exist
884     verifyPropertyCount(element, propertyCount);
885 
886     return std::make_tuple(std::move(rules), std::move(chassis));
887 }
888 
889 std::unique_ptr<Rule> parseRule(const json& element)
890 {
891     verifyIsObject(element);
892     unsigned int propertyCount{0};
893 
894     // Optional comments property; value not stored
895     if (element.contains("comments"))
896     {
897         ++propertyCount;
898     }
899 
900     // Required id property
901     const json& idElement = getRequiredProperty(element, "id");
902     std::string id = parseString(idElement);
903     ++propertyCount;
904 
905     // Required actions property
906     const json& actionsElement = getRequiredProperty(element, "actions");
907     std::vector<std::unique_ptr<Action>> actions =
908         parseActionArray(actionsElement);
909     ++propertyCount;
910 
911     // Verify no invalid properties exist
912     verifyPropertyCount(element, propertyCount);
913 
914     return std::make_unique<Rule>(id, std::move(actions));
915 }
916 
917 std::vector<std::unique_ptr<Rule>> parseRuleArray(const json& element)
918 {
919     verifyIsArray(element);
920     std::vector<std::unique_ptr<Rule>> rules;
921     for (auto& ruleElement : element)
922     {
923         rules.emplace_back(parseRule(ruleElement));
924     }
925     return rules;
926 }
927 
928 std::vector<std::unique_ptr<Action>>
929     parseRuleIDOrActionsProperty(const json& element)
930 {
931     verifyIsObject(element);
932     // Required rule_id or actions property
933     std::vector<std::unique_ptr<Action>> actions{};
934     auto ruleIDIt = element.find("rule_id");
935     auto actionsIt = element.find("actions");
936     if ((actionsIt == element.end()) && (ruleIDIt != element.end()))
937     {
938         std::string ruleID = parseString(*ruleIDIt);
939         actions.emplace_back(std::make_unique<RunRuleAction>(ruleID));
940     }
941     else if ((actionsIt != element.end()) && (ruleIDIt == element.end()))
942     {
943         actions = parseActionArray(*actionsIt);
944     }
945     else
946     {
947         throw std::invalid_argument{"Invalid property combination: Must "
948                                     "contain either rule_id or actions"};
949     }
950 
951     return actions;
952 }
953 
954 std::unique_ptr<RunRuleAction> parseRunRule(const json& element)
955 {
956     // String ruleID
957     std::string ruleID = parseString(element);
958 
959     return std::make_unique<RunRuleAction>(ruleID);
960 }
961 
962 pmbus_utils::SensorDataFormat parseSensorDataFormat(const json& element)
963 {
964     if (!element.is_string())
965     {
966         throw std::invalid_argument{"Element is not a string"};
967     }
968     std::string value = element.get<std::string>();
969     pmbus_utils::SensorDataFormat format{};
970 
971     if (value == "linear_11")
972     {
973         format = pmbus_utils::SensorDataFormat::linear_11;
974     }
975     else if (value == "linear_16")
976     {
977         format = pmbus_utils::SensorDataFormat::linear_16;
978     }
979     else
980     {
981         throw std::invalid_argument{"Element is not a sensor data format"};
982     }
983 
984     return format;
985 }
986 
987 std::unique_ptr<SensorMonitoring> parseSensorMonitoring(const json& element)
988 {
989     verifyIsObject(element);
990     unsigned int propertyCount{0};
991 
992     // Optional comments property; value not stored
993     if (element.contains("comments"))
994     {
995         ++propertyCount;
996     }
997 
998     // Required rule_id or actions property
999     std::vector<std::unique_ptr<Action>> actions{};
1000     actions = parseRuleIDOrActionsProperty(element);
1001     ++propertyCount;
1002 
1003     // Verify no invalid properties exist
1004     verifyPropertyCount(element, propertyCount);
1005 
1006     return std::make_unique<SensorMonitoring>(std::move(actions));
1007 }
1008 
1009 SensorType parseSensorType(const json& element)
1010 {
1011     if (!element.is_string())
1012     {
1013         throw std::invalid_argument{"Element is not a string"};
1014     }
1015     std::string value = element.get<std::string>();
1016     SensorType type{};
1017 
1018     if (value == "iout")
1019     {
1020         type = SensorType::iout;
1021     }
1022     else if (value == "iout_peak")
1023     {
1024         type = SensorType::iout_peak;
1025     }
1026     else if (value == "iout_valley")
1027     {
1028         type = SensorType::iout_valley;
1029     }
1030     else if (value == "pout")
1031     {
1032         type = SensorType::pout;
1033     }
1034     else if (value == "temperature")
1035     {
1036         type = SensorType::temperature;
1037     }
1038     else if (value == "temperature_peak")
1039     {
1040         type = SensorType::temperature_peak;
1041     }
1042     else if (value == "vout")
1043     {
1044         type = SensorType::vout;
1045     }
1046     else if (value == "vout_peak")
1047     {
1048         type = SensorType::vout_peak;
1049     }
1050     else if (value == "vout_valley")
1051     {
1052         type = SensorType::vout_valley;
1053     }
1054     else
1055     {
1056         throw std::invalid_argument{"Element is not a sensor type"};
1057     }
1058 
1059     return type;
1060 }
1061 
1062 std::unique_ptr<SetDeviceAction> parseSetDevice(const json& element)
1063 {
1064     // String deviceID
1065     std::string deviceID = parseString(element);
1066 
1067     return std::make_unique<SetDeviceAction>(deviceID);
1068 }
1069 
1070 pmbus_utils::VoutDataFormat parseVoutDataFormat(const json& element)
1071 {
1072     if (!element.is_string())
1073     {
1074         throw std::invalid_argument{"Element is not a string"};
1075     }
1076     std::string value = element.get<std::string>();
1077     pmbus_utils::VoutDataFormat format{};
1078 
1079     if (value == "linear")
1080     {
1081         format = pmbus_utils::VoutDataFormat::linear;
1082     }
1083     else if (value == "vid")
1084     {
1085         format = pmbus_utils::VoutDataFormat::vid;
1086     }
1087     else if (value == "direct")
1088     {
1089         format = pmbus_utils::VoutDataFormat::direct;
1090     }
1091     else if (value == "ieee")
1092     {
1093         format = pmbus_utils::VoutDataFormat::ieee;
1094     }
1095     else
1096     {
1097         throw std::invalid_argument{"Element is not a vout data format"};
1098     }
1099 
1100     return format;
1101 }
1102 
1103 } // namespace internal
1104 
1105 } // namespace phosphor::power::regulators::config_file_parser
1106