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 #pragma once
17 
18 #include "action.hpp"
19 #include "and_action.hpp"
20 #include "chassis.hpp"
21 #include "compare_presence_action.hpp"
22 #include "compare_vpd_action.hpp"
23 #include "configuration.hpp"
24 #include "device.hpp"
25 #include "i2c_compare_bit_action.hpp"
26 #include "i2c_compare_byte_action.hpp"
27 #include "i2c_compare_bytes_action.hpp"
28 #include "i2c_interface.hpp"
29 #include "i2c_write_bit_action.hpp"
30 #include "i2c_write_byte_action.hpp"
31 #include "i2c_write_bytes_action.hpp"
32 #include "if_action.hpp"
33 #include "not_action.hpp"
34 #include "or_action.hpp"
35 #include "pmbus_read_sensor_action.hpp"
36 #include "pmbus_write_vout_command_action.hpp"
37 #include "presence_detection.hpp"
38 #include "rail.hpp"
39 #include "rule.hpp"
40 #include "run_rule_action.hpp"
41 #include "sensor_monitoring.hpp"
42 #include "set_device_action.hpp"
43 
44 #include <nlohmann/json.hpp>
45 
46 #include <cstdint>
47 #include <filesystem>
48 #include <memory>
49 #include <stdexcept>
50 #include <string>
51 #include <tuple>
52 #include <vector>
53 
54 namespace phosphor::power::regulators::config_file_parser
55 {
56 
57 /**
58  * Parses the specified JSON configuration file.
59  *
60  * Returns the corresponding C++ Rule and Chassis objects.
61  *
62  * Throws a ConfigFileParserError if an error occurs.
63  *
64  * @param pathName configuration file path name
65  * @return tuple containing vectors of Rule and Chassis objects
66  */
67 std::tuple<std::vector<std::unique_ptr<Rule>>,
68            std::vector<std::unique_ptr<Chassis>>>
69     parse(const std::filesystem::path& pathName);
70 
71 /*
72  * Internal implementation details for parse()
73  */
74 namespace internal
75 {
76 
77 /**
78  * Returns the specified property of the specified JSON element.
79  *
80  * Throws an invalid_argument exception if the property does not exist.
81  *
82  * @param element JSON element
83  * @param property property name
84  */
85 inline const nlohmann::json& getRequiredProperty(const nlohmann::json& element,
86                                                  const std::string& property)
87 {
88     auto it = element.find(property);
89     if (it == element.end())
90     {
91         throw std::invalid_argument{"Required property missing: " + property};
92     }
93     return *it;
94 }
95 
96 /**
97  * Parses a JSON element containing an action.
98  *
99  * Returns the corresponding C++ Action object.
100  *
101  * Throws an exception if parsing fails.
102  *
103  * @param element JSON element
104  * @return Action object
105  */
106 std::unique_ptr<Action> parseAction(const nlohmann::json& element);
107 
108 /**
109  * Parses a JSON element containing an array of actions.
110  *
111  * Returns the corresponding C++ Action objects.
112  *
113  * Throws an exception if parsing fails.
114  *
115  * @param element JSON element
116  * @return vector of Action objects
117  */
118 std::vector<std::unique_ptr<Action>>
119     parseActionArray(const nlohmann::json& element);
120 
121 /**
122  * Parses a JSON element containing an and action.
123  *
124  * Returns the corresponding C++ AndAction object.
125  *
126  * Throws an exception if parsing fails.
127  *
128  * @param element JSON element
129  * @return AndAction object
130  */
131 std::unique_ptr<AndAction> parseAnd(const nlohmann::json& element);
132 
133 /**
134  * Parses a JSON element containing a bit position (from 0-7).
135  *
136  * Returns the corresponding C++ uint8_t value.
137  *
138  * Throws an exception if parsing fails.
139  *
140  * @param element JSON element
141  * @return uint8_t value
142  */
143 inline uint8_t parseBitPosition(const nlohmann::json& element)
144 {
145     // Verify element contains an integer
146     if (!element.is_number_integer())
147     {
148         throw std::invalid_argument{"Element is not an integer"};
149     }
150     int value = element.get<int>();
151     if ((value < 0) || (value > 7))
152     {
153         throw std::invalid_argument{"Element is not a bit position"};
154     }
155     return static_cast<uint8_t>(value);
156 }
157 
158 /**
159  * Parses a JSON element containing a bit value (0 or 1).
160  *
161  * Returns the corresponding C++ uint8_t value.
162  *
163  * Throws an exception if parsing fails.
164  *
165  * @param element JSON element
166  * @return uint8_t value
167  */
168 inline uint8_t parseBitValue(const nlohmann::json& element)
169 {
170     // Verify element contains an integer
171     if (!element.is_number_integer())
172     {
173         throw std::invalid_argument{"Element is not an integer"};
174     }
175     int value = element.get<int>();
176     if ((value < 0) || (value > 1))
177     {
178         throw std::invalid_argument{"Element is not a bit value"};
179     }
180     return static_cast<uint8_t>(value);
181 }
182 
183 /**
184  * Parses a JSON element containing a boolean.
185  *
186  * Returns the corresponding C++ boolean value.
187  *
188  * Throws an exception if parsing fails.
189  *
190  * @param element JSON element
191  * @return boolean value
192  */
193 inline bool parseBoolean(const nlohmann::json& element)
194 {
195     // Verify element contains a boolean
196     if (!element.is_boolean())
197     {
198         throw std::invalid_argument{"Element is not a boolean"};
199     }
200     return element.get<bool>();
201 }
202 
203 /**
204  * Parses a JSON element containing a chassis.
205  *
206  * Returns the corresponding C++ Chassis object.
207  *
208  * Throws an exception if parsing fails.
209  *
210  * @param element JSON element
211  * @return Chassis object
212  */
213 std::unique_ptr<Chassis> parseChassis(const nlohmann::json& element);
214 
215 /**
216  * Parses a JSON element containing an array of chassis.
217  *
218  * Returns the corresponding C++ Chassis objects.
219  *
220  * Throws an exception if parsing fails.
221  *
222  * @param element JSON element
223  * @return vector of Chassis objects
224  */
225 std::vector<std::unique_ptr<Chassis>>
226     parseChassisArray(const nlohmann::json& element);
227 
228 /**
229  * Parses a JSON element containing a compare_presence action.
230  *
231  * Returns the corresponding C++ ComparePresenceAction object.
232  *
233  * Throws an exception if parsing fails.
234  *
235  * @param element JSON element
236  * @return ComparePresenceAction object
237  */
238 std::unique_ptr<ComparePresenceAction>
239     parseComparePresence(const nlohmann::json& element);
240 
241 /**
242  * Parses a JSON element containing a compare_vpd action.
243  *
244  * Returns the corresponding C++ CompareVPDAction object.
245  *
246  * Throws an exception if parsing fails.
247  *
248  * @param element JSON element
249  * @return CompareVPDAction object
250  */
251 std::unique_ptr<CompareVPDAction>
252     parseCompareVPD(const nlohmann::json& element);
253 
254 /**
255  * Parses a JSON element containing a configuration.
256  *
257  * Returns the corresponding C++ Configuration object.
258  *
259  * Throws an exception if parsing fails.
260  *
261  * @param element JSON element
262  * @return Configuration object
263  */
264 std::unique_ptr<Configuration>
265     parseConfiguration(const nlohmann::json& element);
266 
267 /**
268  * Parses a JSON element containing a device.
269  *
270  * Returns the corresponding C++ Device object.
271  *
272  * Throws an exception if parsing fails.
273  *
274  * @param element JSON element
275  * @return Device object
276  */
277 std::unique_ptr<Device> parseDevice(const nlohmann::json& element);
278 
279 /**
280  * Parses a JSON element containing an array of devices.
281  *
282  * Returns the corresponding C++ Device objects.
283  *
284  * Throws an exception if parsing fails.
285  *
286  * @param element JSON element
287  * @return vector of Device objects
288  */
289 std::vector<std::unique_ptr<Device>>
290     parseDeviceArray(const nlohmann::json& element);
291 
292 /**
293  * Parses a JSON element containing a double (floating point number).
294  *
295  * Returns the corresponding C++ double value.
296  *
297  * Throws an exception if parsing fails.
298  *
299  * @param element JSON element
300  * @return double value
301  */
302 inline double parseDouble(const nlohmann::json& element)
303 {
304     // Verify element contains a number (integer or floating point)
305     if (!element.is_number())
306     {
307         throw std::invalid_argument{"Element is not a number"};
308     }
309     return element.get<double>();
310 }
311 
312 /**
313  * Parses a JSON element containing a byte value expressed as a hexadecimal
314  * string.
315  *
316  * The JSON number data type does not support the hexadecimal format.  For this
317  * reason, hexadecimal byte values are stored as strings in the configuration
318  * file.
319  *
320  * Returns the corresponding C++ uint8_t value.
321  *
322  * Throws an exception if parsing fails.
323  *
324  * @param element JSON element
325  * @return uint8_t value
326  */
327 inline uint8_t parseHexByte(const nlohmann::json& element)
328 {
329     if (!element.is_string())
330     {
331         throw std::invalid_argument{"Element is not a string"};
332     }
333     std::string value = element.get<std::string>();
334 
335     bool isHex = (value.compare(0, 2, "0x") == 0) && (value.size() > 2) &&
336                  (value.size() < 5) &&
337                  (value.find_first_not_of("0123456789abcdefABCDEF", 2) ==
338                   std::string::npos);
339     if (!isHex)
340     {
341         throw std::invalid_argument{"Element is not hexadecimal string"};
342     }
343     return static_cast<uint8_t>(std::stoul(value, 0, 0));
344 }
345 
346 /**
347  * Parses a JSON element containing an array of byte values expressed as a
348  * hexadecimal strings.
349  *
350  * Returns the corresponding C++ uint8_t values.
351  *
352  * Throws an exception if parsing fails.
353  *
354  * @param element JSON element
355  * @return vector of uint8_t
356  */
357 std::vector<uint8_t> parseHexByteArray(const nlohmann::json& element);
358 
359 /**
360  * Parses a JSON element containing an i2c_compare_bit action.
361  *
362  * Returns the corresponding C++ I2CCompareBitAction object.
363  *
364  * Throws an exception if parsing fails.
365  *
366  * @param element JSON element
367  * @return I2CCompareBitAction object
368  */
369 std::unique_ptr<I2CCompareBitAction>
370     parseI2CCompareBit(const nlohmann::json& element);
371 
372 /**
373  * Parses a JSON element containing an i2c_compare_byte action.
374  *
375  * Returns the corresponding C++ I2CCompareByteAction object.
376  *
377  * Throws an exception if parsing fails.
378  *
379  * @param element JSON element
380  * @return I2CCompareByteAction object
381  */
382 std::unique_ptr<I2CCompareByteAction>
383     parseI2CCompareByte(const nlohmann::json& element);
384 
385 /**
386  * Parses a JSON element containing an i2c_compare_bytes action.
387  *
388  * Returns the corresponding C++ I2CCompareBytesAction object.
389  *
390  * Throws an exception if parsing fails.
391  *
392  * @param element JSON element
393  * @return I2CCompareBytesAction object
394  */
395 std::unique_ptr<I2CCompareBytesAction>
396     parseI2CCompareBytes(const nlohmann::json& element);
397 
398 /**
399  * Parses a JSON element containing an i2c_interface.
400  *
401  * Returns the corresponding C++ i2c::I2CInterface object.
402  *
403  * Throws an exception if parsing fails.
404  *
405  * @param element JSON element
406  * @return i2c::I2CInterface object
407  */
408 std::unique_ptr<i2c::I2CInterface>
409     parseI2CInterface(const nlohmann::json& element);
410 
411 /**
412  * Parses a JSON element containing an i2c_write_bit action.
413  *
414  * Returns the corresponding C++ I2CWriteBitAction object.
415  *
416  * Throws an exception if parsing fails.
417  *
418  * @param element JSON element
419  * @return I2CWriteBitAction object
420  */
421 std::unique_ptr<I2CWriteBitAction>
422     parseI2CWriteBit(const nlohmann::json& element);
423 
424 /**
425  * Parses a JSON element containing an i2c_write_byte action.
426  *
427  * Returns the corresponding C++ I2CWriteByteAction object.
428  *
429  * Throws an exception if parsing fails.
430  *
431  * @param element JSON element
432  * @return I2CWriteByteAction object
433  */
434 std::unique_ptr<I2CWriteByteAction>
435     parseI2CWriteByte(const nlohmann::json& element);
436 
437 /**
438  * Parses a JSON element containing an i2c_write_bytes action.
439  *
440  * Returns the corresponding C++ I2CWriteBytesAction object.
441  *
442  * Throws an exception if parsing fails.
443  *
444  * @param element JSON element
445  * @return I2CWriteBytesAction object
446  */
447 std::unique_ptr<I2CWriteBytesAction>
448     parseI2CWriteBytes(const nlohmann::json& element);
449 
450 /**
451  * Parses a JSON element containing an if action.
452  *
453  * Returns the corresponding C++ IfAction object.
454  *
455  * Throws an exception if parsing fails.
456  *
457  * @param element JSON element
458  * @return IfAction object
459  */
460 std::unique_ptr<IfAction> parseIf(const nlohmann::json& element);
461 
462 /**
463  * Parses a JSON element containing an 8-bit signed integer.
464  *
465  * Returns the corresponding C++ int8_t value.
466  *
467  * Throws an exception if parsing fails.
468  *
469  * @param element JSON element
470  * @return int8_t value
471  */
472 inline int8_t parseInt8(const nlohmann::json& element)
473 {
474     // Verify element contains an integer
475     if (!element.is_number_integer())
476     {
477         throw std::invalid_argument{"Element is not an integer"};
478     }
479     int value = element.get<int>();
480     if ((value < INT8_MIN) || (value > INT8_MAX))
481     {
482         throw std::invalid_argument{"Element is not an 8-bit signed integer"};
483     }
484     return static_cast<int8_t>(value);
485 }
486 
487 /**
488  * Parses a JSON element containing a not action.
489  *
490  * Returns the corresponding C++ NotAction object.
491  *
492  * Throws an exception if parsing fails.
493  *
494  * @param element JSON element
495  * @return NotAction object
496  */
497 std::unique_ptr<NotAction> parseNot(const nlohmann::json& element);
498 
499 /**
500  * Parses a JSON element containing an or action.
501  *
502  * Returns the corresponding C++ OrAction object.
503  *
504  * Throws an exception if parsing fails.
505  *
506  * @param element JSON element
507  * @return OrAction object
508  */
509 std::unique_ptr<OrAction> parseOr(const nlohmann::json& element);
510 
511 /**
512  * Parses a JSON element containing a pmbus_read_sensor action.
513  *
514  * Returns the corresponding C++ PMBusReadSensorAction object.
515  *
516  * Throws an exception if parsing fails.
517  *
518  * @param element JSON element
519  * @return PMBusReadSensorAction object
520  */
521 std::unique_ptr<PMBusReadSensorAction>
522     parsePMBusReadSensor(const nlohmann::json& element);
523 
524 /**
525  * Parses a JSON element containing a pmbus_write_vout_command action.
526  *
527  * Returns the corresponding C++ PMBusWriteVoutCommandAction object.
528  *
529  * Throws an exception if parsing fails.
530  *
531  * @param element JSON element
532  * @return PMBusWriteVoutCommandAction object
533  */
534 std::unique_ptr<PMBusWriteVoutCommandAction>
535     parsePMBusWriteVoutCommand(const nlohmann::json& element);
536 
537 /**
538  * Parses a JSON element containing a presence detection operation.
539  *
540  * Returns the corresponding C++ PresenceDetection object.
541  *
542  * Throws an exception if parsing fails.
543  *
544  * @param element JSON element
545  * @return PresenceDetection object
546  */
547 std::unique_ptr<PresenceDetection>
548     parsePresenceDetection(const nlohmann::json& element);
549 
550 /**
551  * Parses a JSON element containing a rail.
552  *
553  * Returns the corresponding C++ Rail object.
554  *
555  * Throws an exception if parsing fails.
556  *
557  * @param element JSON element
558  * @return Rail object
559  */
560 std::unique_ptr<Rail> parseRail(const nlohmann::json& element);
561 
562 /**
563  * Parses a JSON element containing an array of rails.
564  *
565  * Returns the corresponding C++ Rail objects.
566  *
567  * Throws an exception if parsing fails.
568  *
569  * @param element JSON element
570  * @return vector of Rail objects
571  */
572 std::vector<std::unique_ptr<Rail>>
573     parseRailArray(const nlohmann::json& element);
574 
575 /**
576  * Parses the JSON root element of the entire configuration file.
577  *
578  * Returns the corresponding C++ Rule and Chassis objects.
579  *
580  * Throws an exception if parsing fails.
581  *
582  * @param element JSON element
583  * @return tuple containing vectors of Rule and Chassis objects
584  */
585 std::tuple<std::vector<std::unique_ptr<Rule>>,
586            std::vector<std::unique_ptr<Chassis>>>
587     parseRoot(const nlohmann::json& element);
588 
589 /**
590  * Parses a JSON element containing a rule.
591  *
592  * Returns the corresponding C++ Rule object.
593  *
594  * Throws an exception if parsing fails.
595  *
596  * @param element JSON element
597  * @return Rule object
598  */
599 std::unique_ptr<Rule> parseRule(const nlohmann::json& element);
600 
601 /**
602  * Parses a JSON element containing an array of rules.
603  *
604  * Returns the corresponding C++ Rule objects.
605  *
606  * Throws an exception if parsing fails.
607  *
608  * @param element JSON element
609  * @return vector of Rule objects
610  */
611 std::vector<std::unique_ptr<Rule>>
612     parseRuleArray(const nlohmann::json& element);
613 
614 /**
615  * Parses the "rule_id" or "actions" property in a JSON element.
616  *
617  * The element must contain one property or the other but not both.
618  *
619  * If the element contains a "rule_id" property, the corresponding C++
620  * RunRuleAction object is returned.
621  *
622  * If the element contains an "actions" property, the corresponding C++ Action
623  * objects are returned.
624  *
625  * Throws an exception if parsing fails.
626  *
627  * @param element JSON element
628  * @return vector of Action objects
629  */
630 std::vector<std::unique_ptr<Action>>
631     parseRuleIDOrActionsProperty(const nlohmann::json& element);
632 
633 /**
634  * Parses a JSON element containing a run_rule action.
635  *
636  * Returns the corresponding C++ RunRuleAction object.
637  *
638  * Throws an exception if parsing fails.
639  *
640  * @param element JSON element
641  * @return RunRuleAction object
642  */
643 std::unique_ptr<RunRuleAction> parseRunRule(const nlohmann::json& element);
644 
645 /**
646  * Parses a JSON element containing a SensorDataFormat expressed as a string.
647  *
648  * Returns the corresponding SensorDataFormat enum value.
649  *
650  * Throws an exception if parsing fails.
651  *
652  * @param element JSON element
653  * @return SensorDataFormat enum value
654  */
655 pmbus_utils::SensorDataFormat
656     parseSensorDataFormat(const nlohmann::json& element);
657 
658 /**
659  * Parses a JSON element containing a sensor monitoring operation.
660  *
661  * Returns the corresponding C++ SensorMonitoring object.
662  *
663  * Throws an exception if parsing fails.
664  *
665  * @param element JSON element
666  * @return SensorMonitoring object
667  */
668 std::unique_ptr<SensorMonitoring>
669     parseSensorMonitoring(const nlohmann::json& element);
670 
671 /**
672  * Parses a JSON element containing a SensorValueType expressed as a string.
673  *
674  * Returns the corresponding SensorValueType enum value.
675  *
676  * Throws an exception if parsing fails.
677  *
678  * @param element JSON element
679  * @return SensorValueType enum value
680  */
681 pmbus_utils::SensorValueType
682     parseSensorValueType(const nlohmann::json& element);
683 
684 /**
685  * Parses a JSON element containing a set_device action.
686  *
687  * Returns the corresponding C++ SetDeviceAction object.
688  *
689  * Throws an exception if parsing fails.
690  *
691  * @param element JSON element
692  * @return SetDeviceAction object
693  */
694 std::unique_ptr<SetDeviceAction> parseSetDevice(const nlohmann::json& element);
695 
696 /**
697  * Parses a JSON element containing a string.
698  *
699  * Returns the corresponding C++ string.
700  *
701  * Throws an exception if parsing fails.
702  *
703  * @param element JSON element
704  * @param isEmptyValid indicates whether an empty string value is valid
705  * @return string value
706  */
707 inline std::string parseString(const nlohmann::json& element,
708                                bool isEmptyValid = false)
709 {
710     if (!element.is_string())
711     {
712         throw std::invalid_argument{"Element is not a string"};
713     }
714     std::string value = element.get<std::string>();
715     if (value.empty() && !isEmptyValid)
716     {
717         throw std::invalid_argument{"Element contains an empty string"};
718     }
719     return value;
720 }
721 
722 /**
723  * Parses a JSON element containing an 8-bit unsigned integer.
724  *
725  * Returns the corresponding C++ uint8_t value.
726  *
727  * Throws an exception if parsing fails.
728  *
729  * @param element JSON element
730  * @return uint8_t value
731  */
732 inline uint8_t parseUint8(const nlohmann::json& element)
733 {
734     // Verify element contains an integer
735     if (!element.is_number_integer())
736     {
737         throw std::invalid_argument{"Element is not an integer"};
738     }
739     int value = element.get<int>();
740     if ((value < 0) || (value > UINT8_MAX))
741     {
742         throw std::invalid_argument{"Element is not an 8-bit unsigned integer"};
743     }
744     return static_cast<uint8_t>(value);
745 }
746 
747 /**
748  * Parses a JSON element containing an unsigned integer.
749  *
750  * Returns the corresponding C++ unsigned int value.
751  *
752  * Throws an exception if parsing fails.
753  *
754  * @param element JSON element
755  * @return unsigned int value
756  */
757 inline unsigned int parseUnsignedInteger(const nlohmann::json& element)
758 {
759     // Verify element contains an unsigned integer
760     if (!element.is_number_unsigned())
761     {
762         throw std::invalid_argument{"Element is not an unsigned integer"};
763     }
764     return element.get<unsigned int>();
765 }
766 
767 /**
768  * Parses a JSON element containing a VoutDataFormat expressed as a string.
769  *
770  * Returns the corresponding VoutDataFormat enum value.
771  *
772  * Throws an exception if parsing fails.
773  *
774  * @param element JSON element
775  * @return VoutDataFormat enum value
776  */
777 pmbus_utils::VoutDataFormat parseVoutDataFormat(const nlohmann::json& element);
778 
779 /**
780  * Verifies that the specified JSON element is a JSON array.
781  *
782  * Throws an invalid_argument exception if the element is not an array.
783  *
784  * @param element JSON element
785  */
786 inline void verifyIsArray(const nlohmann::json& element)
787 {
788     if (!element.is_array())
789     {
790         throw std::invalid_argument{"Element is not an array"};
791     }
792 }
793 
794 /**
795  * Verifies that the specified JSON element is a JSON object.
796  *
797  * Throws an invalid_argument exception if the element is not an object.
798  *
799  * @param element JSON element
800  */
801 inline void verifyIsObject(const nlohmann::json& element)
802 {
803     if (!element.is_object())
804     {
805         throw std::invalid_argument{"Element is not an object"};
806     }
807 }
808 
809 /**
810  * Verifies that the specified JSON element contains the expected number of
811  * properties.
812  *
813  * Throws an invalid_argument exception if the element contains a different
814  * number of properties.  This indicates the element contains an invalid
815  * property.
816  *
817  * @param element JSON element
818  * @param expectedCount expected number of properties in element
819  */
820 inline void verifyPropertyCount(const nlohmann::json& element,
821                                 unsigned int expectedCount)
822 {
823     if (element.size() != expectedCount)
824     {
825         throw std::invalid_argument{"Element contains an invalid property"};
826     }
827 }
828 
829 } // namespace internal
830 
831 } // namespace phosphor::power::regulators::config_file_parser
832