xref: /openbmc/phosphor-led-manager/manager/json-parser.hpp (revision 55badf7979f57d5f9ed2db726eebf35e84d0dac5)
1953315d2SPatrick Williams #include "config.h"
2953315d2SPatrick Williams 
37ba70c82SAlexander Hansen #include "grouplayout.hpp"
4953315d2SPatrick Williams #include "json-config.hpp"
5953315d2SPatrick Williams #include "ledlayout.hpp"
6953315d2SPatrick Williams 
7953315d2SPatrick Williams #include <nlohmann/json.hpp>
8953315d2SPatrick Williams #include <phosphor-logging/lg2.hpp>
9953315d2SPatrick Williams #include <sdbusplus/bus.hpp>
10953315d2SPatrick Williams #include <sdeventplus/event.hpp>
11953315d2SPatrick Williams 
12953315d2SPatrick Williams #include <filesystem>
13953315d2SPatrick Williams #include <fstream>
14953315d2SPatrick Williams #include <iostream>
15953315d2SPatrick Williams 
16953315d2SPatrick Williams namespace fs = std::filesystem;
17953315d2SPatrick Williams 
18953315d2SPatrick Williams using Json = nlohmann::json;
19953315d2SPatrick Williams 
20953315d2SPatrick Williams // Priority for a particular LED needs to stay SAME across all groups
21953315d2SPatrick Williams // phosphor::led::Layout::Action can only be one of `Blink` and `On`
22f2044037SPatrick Williams using PriorityMap =
23*55badf79SAlexander Hansen     std::unordered_map<std::string,
24*55badf79SAlexander Hansen                        std::optional<phosphor::led::Layout::Action>>;
25953315d2SPatrick Williams 
26953315d2SPatrick Williams /** @brief Parse LED JSON file and output Json object
27953315d2SPatrick Williams  *
28953315d2SPatrick Williams  *  @param[in] path - path of LED JSON file
29953315d2SPatrick Williams  *
30953315d2SPatrick Williams  *  @return const Json - Json object
31953315d2SPatrick Williams  */
3273ff9ad6SPatrick Williams Json readJson(const fs::path& path)
33953315d2SPatrick Williams {
34953315d2SPatrick Williams     if (!fs::exists(path) || fs::is_empty(path))
35953315d2SPatrick Williams     {
36953315d2SPatrick Williams         lg2::error("Incorrect File Path or empty file, FILE_PATH = {PATH}",
37953315d2SPatrick Williams                    "PATH", path);
38953315d2SPatrick Williams         throw std::runtime_error("Incorrect File Path or empty file");
39953315d2SPatrick Williams     }
40953315d2SPatrick Williams 
41953315d2SPatrick Williams     try
42953315d2SPatrick Williams     {
43953315d2SPatrick Williams         std::ifstream jsonFile(path);
44953315d2SPatrick Williams         return Json::parse(jsonFile);
45953315d2SPatrick Williams     }
46953315d2SPatrick Williams     catch (const std::exception& e)
47953315d2SPatrick Williams     {
48953315d2SPatrick Williams         lg2::error(
49953315d2SPatrick Williams             "Failed to parse config file, ERROR = {ERROR}, FILE_PATH = {PATH}",
50953315d2SPatrick Williams             "ERROR", e, "PATH", path);
51953315d2SPatrick Williams         throw std::runtime_error("Failed to parse config file");
52953315d2SPatrick Williams     }
53953315d2SPatrick Williams }
54953315d2SPatrick Williams 
55953315d2SPatrick Williams /** @brief Returns action enum based on string
56953315d2SPatrick Williams  *
57953315d2SPatrick Williams  *  @param[in] action - action string
58953315d2SPatrick Williams  *
59953315d2SPatrick Williams  *  @return Action - action enum (On/Blink)
60953315d2SPatrick Williams  */
61953315d2SPatrick Williams phosphor::led::Layout::Action getAction(const std::string& action)
62953315d2SPatrick Williams {
63953315d2SPatrick Williams     assert(action == "On" || action == "Blink");
64953315d2SPatrick Williams 
65ed80e885SPatrick Williams     return action == "Blink" ? phosphor::led::Layout::Action::Blink
66ed80e885SPatrick Williams                              : phosphor::led::Layout::Action::On;
67953315d2SPatrick Williams }
68953315d2SPatrick Williams 
69*55badf79SAlexander Hansen static std::string priorityToString(
70*55badf79SAlexander Hansen     const std::optional<phosphor::led::Layout::Action>& priority)
71*55badf79SAlexander Hansen {
72*55badf79SAlexander Hansen     if (!priority.has_value())
73*55badf79SAlexander Hansen     {
74*55badf79SAlexander Hansen         return "none";
75*55badf79SAlexander Hansen     }
76*55badf79SAlexander Hansen     switch (priority.value())
77*55badf79SAlexander Hansen     {
78*55badf79SAlexander Hansen         case phosphor::led::Layout::Action::Off:
79*55badf79SAlexander Hansen             return "Off";
80*55badf79SAlexander Hansen         case phosphor::led::Layout::Action::On:
81*55badf79SAlexander Hansen             return "On";
82*55badf79SAlexander Hansen         case phosphor::led::Layout::Action::Blink:
83*55badf79SAlexander Hansen             return "Blink";
84*55badf79SAlexander Hansen     }
85*55badf79SAlexander Hansen     return "?";
86*55badf79SAlexander Hansen }
87*55badf79SAlexander Hansen 
88953315d2SPatrick Williams /** @brief Validate the Priority of an LED is same across ALL groups
89953315d2SPatrick Williams  *
90953315d2SPatrick Williams  *  @param[in] name - led name member of each group
91953315d2SPatrick Williams  *  @param[in] priority - member priority of each group
92f2044037SPatrick Williams  *  @param[out] priorityMap - std::unordered_map, key:name, value:priority
93953315d2SPatrick Williams  *
94953315d2SPatrick Williams  *  @return
95953315d2SPatrick Williams  */
96*55badf79SAlexander Hansen void validatePriority(
97*55badf79SAlexander Hansen     const std::string& name,
98*55badf79SAlexander Hansen     const std::optional<phosphor::led::Layout::Action>& priority,
99953315d2SPatrick Williams     PriorityMap& priorityMap)
100953315d2SPatrick Williams {
101953315d2SPatrick Williams     auto iter = priorityMap.find(name);
102953315d2SPatrick Williams     if (iter == priorityMap.end())
103953315d2SPatrick Williams     {
104953315d2SPatrick Williams         priorityMap.emplace(name, priority);
105953315d2SPatrick Williams         return;
106953315d2SPatrick Williams     }
107953315d2SPatrick Williams 
108953315d2SPatrick Williams     if (iter->second != priority)
109953315d2SPatrick Williams     {
110953315d2SPatrick Williams         lg2::error(
111953315d2SPatrick Williams             "Priority of LED is not same across all, Name = {NAME}, Old Priority = {OLD_PRIO}, New Priority = {NEW_PRIO}",
112*55badf79SAlexander Hansen             "NAME", name, "OLD_PRIO", priorityToString(iter->second),
113*55badf79SAlexander Hansen             "NEW_PRIO", priorityToString(priority));
114953315d2SPatrick Williams 
115953315d2SPatrick Williams         throw std::runtime_error(
116953315d2SPatrick Williams             "Priority of at least one LED is not same across groups");
117953315d2SPatrick Williams     }
118953315d2SPatrick Williams }
119953315d2SPatrick Williams 
120d0f80506SAlexander Hansen static void loadJsonConfigV1GroupMember(const Json& member,
121d0f80506SAlexander Hansen                                         PriorityMap& priorityMap,
122d0f80506SAlexander Hansen                                         phosphor::led::ActionSet& ledActions)
123d0f80506SAlexander Hansen {
124d0f80506SAlexander Hansen     auto name = member.value("Name", "");
125d0f80506SAlexander Hansen     auto action = getAction(member.value("Action", ""));
126d0f80506SAlexander Hansen     uint8_t dutyOn = member.value("DutyOn", 50);
127d0f80506SAlexander Hansen     uint16_t period = member.value("Period", 0);
128d0f80506SAlexander Hansen 
129*55badf79SAlexander Hansen     const std::string priorityStr = member.value("Priority", "");
130*55badf79SAlexander Hansen     std::optional<phosphor::led::Layout::Action> priority = std::nullopt;
131*55badf79SAlexander Hansen 
132*55badf79SAlexander Hansen     if (!priorityStr.empty())
133*55badf79SAlexander Hansen     {
134*55badf79SAlexander Hansen         priority = getAction(priorityStr);
135*55badf79SAlexander Hansen     }
136d0f80506SAlexander Hansen 
137d0f80506SAlexander Hansen     // Same LEDs can be part of multiple groups. However, their
138d0f80506SAlexander Hansen     // priorities across groups need to match.
139d0f80506SAlexander Hansen     validatePriority(name, priority, priorityMap);
140d0f80506SAlexander Hansen 
141d0f80506SAlexander Hansen     phosphor::led::Layout::LedAction ledAction{name, action, dutyOn, period,
142d0f80506SAlexander Hansen                                                priority};
143d0f80506SAlexander Hansen     ledActions.emplace(ledAction);
144d0f80506SAlexander Hansen }
145d0f80506SAlexander Hansen 
146d0f80506SAlexander Hansen static void loadJsonConfigV1Group(const Json& entry,
147d0f80506SAlexander Hansen                                   phosphor::led::GroupMap& ledMap,
148d0f80506SAlexander Hansen                                   PriorityMap& priorityMap)
149d0f80506SAlexander Hansen {
150d0f80506SAlexander Hansen     const Json empty{};
151d0f80506SAlexander Hansen 
152d0f80506SAlexander Hansen     fs::path tmpPath("/xyz/openbmc_project/led/groups");
153d0f80506SAlexander Hansen 
154d0f80506SAlexander Hansen     const std::string groupName = entry.value("group", "");
155d0f80506SAlexander Hansen 
156d0f80506SAlexander Hansen     tmpPath /= groupName;
157d0f80506SAlexander Hansen     auto objpath = tmpPath.string();
158d0f80506SAlexander Hansen     auto members = entry.value("members", empty);
1597ba70c82SAlexander Hansen     int priority = entry.value("Priority", 0);
160d0f80506SAlexander Hansen 
161d0f80506SAlexander Hansen     lg2::debug("config for '{GROUP}'", "GROUP", groupName);
162d0f80506SAlexander Hansen 
163d0f80506SAlexander Hansen     phosphor::led::ActionSet ledActions{};
1647ba70c82SAlexander Hansen     phosphor::led::Layout::GroupLayout groupLayout{};
165d0f80506SAlexander Hansen     for (const auto& member : members)
166d0f80506SAlexander Hansen     {
167d0f80506SAlexander Hansen         loadJsonConfigV1GroupMember(member, priorityMap, ledActions);
168d0f80506SAlexander Hansen     }
169d0f80506SAlexander Hansen 
170d0f80506SAlexander Hansen     // Generated an std::unordered_map of LedGroupNames to std::set of LEDs
171d0f80506SAlexander Hansen     // containing the name and properties.
1727ba70c82SAlexander Hansen     groupLayout.actionSet = ledActions;
1737ba70c82SAlexander Hansen     groupLayout.priority = priority;
1747ba70c82SAlexander Hansen 
1757ba70c82SAlexander Hansen     ledMap.emplace(objpath, groupLayout);
176d0f80506SAlexander Hansen }
177d0f80506SAlexander Hansen 
17873ff9ad6SPatrick Williams /** @brief Load JSON config and return led map (JSON version 1)
179953315d2SPatrick Williams  *
180158b2c14SPatrick Williams  *  @return phosphor::led::GroupMap
181953315d2SPatrick Williams  */
182158b2c14SPatrick Williams const phosphor::led::GroupMap loadJsonConfigV1(const Json& json)
183953315d2SPatrick Williams {
184158b2c14SPatrick Williams     phosphor::led::GroupMap ledMap{};
185953315d2SPatrick Williams     PriorityMap priorityMap{};
186953315d2SPatrick Williams 
187953315d2SPatrick Williams     // define the default JSON as empty
188953315d2SPatrick Williams     const Json empty{};
189953315d2SPatrick Williams     auto leds = json.value("leds", empty);
190953315d2SPatrick Williams 
191953315d2SPatrick Williams     for (const auto& entry : leds)
192953315d2SPatrick Williams     {
193d0f80506SAlexander Hansen         loadJsonConfigV1Group(entry, ledMap, priorityMap);
194953315d2SPatrick Williams     }
195953315d2SPatrick Williams 
196953315d2SPatrick Williams     return ledMap;
197953315d2SPatrick Williams }
198953315d2SPatrick Williams 
19973ff9ad6SPatrick Williams /** @brief Load JSON config and return led map
20073ff9ad6SPatrick Williams  *
201158b2c14SPatrick Williams  *  @return phosphor::led::GroupMap
20273ff9ad6SPatrick Williams  */
203158b2c14SPatrick Williams const phosphor::led::GroupMap loadJsonConfig(const fs::path& path)
20473ff9ad6SPatrick Williams {
20573ff9ad6SPatrick Williams     auto json = readJson(path);
20673ff9ad6SPatrick Williams 
20773ff9ad6SPatrick Williams     auto version = json.value("version", 1);
20873ff9ad6SPatrick Williams     switch (version)
20973ff9ad6SPatrick Williams     {
21073ff9ad6SPatrick Williams         case 1:
21173ff9ad6SPatrick Williams             return loadJsonConfigV1(json);
21273ff9ad6SPatrick Williams 
21373ff9ad6SPatrick Williams         default:
21473ff9ad6SPatrick Williams             lg2::error("Unsupported JSON Version: {VERSION}", "VERSION",
21573ff9ad6SPatrick Williams                        version);
21673ff9ad6SPatrick Williams             throw std::runtime_error("Unsupported version");
21773ff9ad6SPatrick Williams     }
21873ff9ad6SPatrick Williams 
219158b2c14SPatrick Williams     return phosphor::led::GroupMap{};
22073ff9ad6SPatrick Williams }
22173ff9ad6SPatrick Williams 
222953315d2SPatrick Williams /** @brief Get led map from LED groups JSON config
223953315d2SPatrick Williams  *
2247217c035SPatrick Williams  *  @param[in] config - Path to the JSON config.
225158b2c14SPatrick Williams  *  @return phosphor::led::GroupMap
2267217c035SPatrick Williams  *
2277217c035SPatrick Williams  *  @note if config is an empty string, daemon will interrogate dbus for
2287217c035SPatrick Williams  *        compatible strings.
229953315d2SPatrick Williams  */
230158b2c14SPatrick Williams const phosphor::led::GroupMap getSystemLedMap(fs::path config)
231953315d2SPatrick Williams {
2327217c035SPatrick Williams     if (config.empty())
233953315d2SPatrick Williams     {
2347217c035SPatrick Williams         config = phosphor::led::getJsonConfig();
235953315d2SPatrick Williams     }
236953315d2SPatrick Williams 
2377217c035SPatrick Williams     return loadJsonConfig(config);
238953315d2SPatrick Williams }
239