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