xref: /openbmc/phosphor-led-manager/manager/json-parser.hpp (revision 4d44a55ef338ff897d7ecd8adfce7cf27ee880a9)
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
21f2044037SPatrick Williams using PriorityMap =
2255badf79SAlexander Hansen     std::unordered_map<std::string,
2355badf79SAlexander Hansen                        std::optional<phosphor::led::Layout::Action>>;
24953315d2SPatrick Williams 
25953315d2SPatrick Williams /** @brief Parse LED JSON file and output Json object
26953315d2SPatrick Williams  *
27953315d2SPatrick Williams  *  @param[in] path - path of LED JSON file
28953315d2SPatrick Williams  *
29953315d2SPatrick Williams  *  @return const Json - Json object
30953315d2SPatrick Williams  */
3173ff9ad6SPatrick Williams Json readJson(const fs::path& path)
32953315d2SPatrick Williams {
33953315d2SPatrick Williams     if (!fs::exists(path) || fs::is_empty(path))
34953315d2SPatrick Williams     {
35953315d2SPatrick Williams         lg2::error("Incorrect File Path or empty file, FILE_PATH = {PATH}",
36953315d2SPatrick Williams                    "PATH", path);
37953315d2SPatrick Williams         throw std::runtime_error("Incorrect File Path or empty file");
38953315d2SPatrick Williams     }
39953315d2SPatrick Williams 
40953315d2SPatrick Williams     try
41953315d2SPatrick Williams     {
42953315d2SPatrick Williams         std::ifstream jsonFile(path);
43953315d2SPatrick Williams         return Json::parse(jsonFile);
44953315d2SPatrick Williams     }
45953315d2SPatrick Williams     catch (const std::exception& e)
46953315d2SPatrick Williams     {
47953315d2SPatrick Williams         lg2::error(
48953315d2SPatrick Williams             "Failed to parse config file, ERROR = {ERROR}, FILE_PATH = {PATH}",
49953315d2SPatrick Williams             "ERROR", e, "PATH", path);
50953315d2SPatrick Williams         throw std::runtime_error("Failed to parse config file");
51953315d2SPatrick Williams     }
52953315d2SPatrick Williams }
53953315d2SPatrick Williams 
54953315d2SPatrick Williams /** @brief Returns action enum based on string
55953315d2SPatrick Williams  *
56953315d2SPatrick Williams  *  @param[in] action - action string
57953315d2SPatrick Williams  *
58*4d44a55eSAlexander Hansen  *  @return Action - action enum (On/Off/Blink)
59953315d2SPatrick Williams  */
60953315d2SPatrick Williams phosphor::led::Layout::Action getAction(const std::string& action)
61953315d2SPatrick Williams {
62*4d44a55eSAlexander Hansen     if (action == "On")
63*4d44a55eSAlexander Hansen     {
64*4d44a55eSAlexander Hansen         return phosphor::led::Layout::Action::On;
65*4d44a55eSAlexander Hansen     }
66*4d44a55eSAlexander Hansen     if (action == "Off")
67*4d44a55eSAlexander Hansen     {
68*4d44a55eSAlexander Hansen         return phosphor::led::Layout::Action::Off;
69*4d44a55eSAlexander Hansen     }
70*4d44a55eSAlexander Hansen     if (action == "Blink")
71*4d44a55eSAlexander Hansen     {
72*4d44a55eSAlexander Hansen         return phosphor::led::Layout::Action::Blink;
73*4d44a55eSAlexander Hansen     }
74953315d2SPatrick Williams 
75*4d44a55eSAlexander Hansen     assert(false);
76*4d44a55eSAlexander Hansen     return phosphor::led::Layout::Action::Blink;
77953315d2SPatrick Williams }
78953315d2SPatrick Williams 
7955badf79SAlexander Hansen static std::string priorityToString(
8055badf79SAlexander Hansen     const std::optional<phosphor::led::Layout::Action>& priority)
8155badf79SAlexander Hansen {
8255badf79SAlexander Hansen     if (!priority.has_value())
8355badf79SAlexander Hansen     {
8455badf79SAlexander Hansen         return "none";
8555badf79SAlexander Hansen     }
8655badf79SAlexander Hansen     switch (priority.value())
8755badf79SAlexander Hansen     {
8855badf79SAlexander Hansen         case phosphor::led::Layout::Action::Off:
8955badf79SAlexander Hansen             return "Off";
9055badf79SAlexander Hansen         case phosphor::led::Layout::Action::On:
9155badf79SAlexander Hansen             return "On";
9255badf79SAlexander Hansen         case phosphor::led::Layout::Action::Blink:
9355badf79SAlexander Hansen             return "Blink";
9455badf79SAlexander Hansen     }
9555badf79SAlexander Hansen     return "?";
9655badf79SAlexander Hansen }
9755badf79SAlexander Hansen 
98953315d2SPatrick Williams /** @brief Validate the Priority of an LED is same across ALL groups
99953315d2SPatrick Williams  *
100953315d2SPatrick Williams  *  @param[in] name - led name member of each group
101953315d2SPatrick Williams  *  @param[in] priority - member priority of each group
102f2044037SPatrick Williams  *  @param[out] priorityMap - std::unordered_map, key:name, value:priority
103953315d2SPatrick Williams  *
104953315d2SPatrick Williams  *  @return
105953315d2SPatrick Williams  */
10655badf79SAlexander Hansen void validatePriority(
10755badf79SAlexander Hansen     const std::string& name,
10855badf79SAlexander Hansen     const std::optional<phosphor::led::Layout::Action>& priority,
109953315d2SPatrick Williams     PriorityMap& priorityMap)
110953315d2SPatrick Williams {
111953315d2SPatrick Williams     auto iter = priorityMap.find(name);
112953315d2SPatrick Williams     if (iter == priorityMap.end())
113953315d2SPatrick Williams     {
114953315d2SPatrick Williams         priorityMap.emplace(name, priority);
115953315d2SPatrick Williams         return;
116953315d2SPatrick Williams     }
117953315d2SPatrick Williams 
118953315d2SPatrick Williams     if (iter->second != priority)
119953315d2SPatrick Williams     {
120953315d2SPatrick Williams         lg2::error(
121953315d2SPatrick Williams             "Priority of LED is not same across all, Name = {NAME}, Old Priority = {OLD_PRIO}, New Priority = {NEW_PRIO}",
12255badf79SAlexander Hansen             "NAME", name, "OLD_PRIO", priorityToString(iter->second),
12355badf79SAlexander Hansen             "NEW_PRIO", priorityToString(priority));
124953315d2SPatrick Williams 
125953315d2SPatrick Williams         throw std::runtime_error(
126953315d2SPatrick Williams             "Priority of at least one LED is not same across groups");
127953315d2SPatrick Williams     }
128953315d2SPatrick Williams }
129953315d2SPatrick Williams 
130d0f80506SAlexander Hansen static void loadJsonConfigV1GroupMember(const Json& member,
131d0f80506SAlexander Hansen                                         PriorityMap& priorityMap,
132d0f80506SAlexander Hansen                                         phosphor::led::ActionSet& ledActions)
133d0f80506SAlexander Hansen {
134d0f80506SAlexander Hansen     auto name = member.value("Name", "");
135d0f80506SAlexander Hansen     auto action = getAction(member.value("Action", ""));
136d0f80506SAlexander Hansen     uint8_t dutyOn = member.value("DutyOn", 50);
137d0f80506SAlexander Hansen     uint16_t period = member.value("Period", 0);
138d0f80506SAlexander Hansen 
13955badf79SAlexander Hansen     const std::string priorityStr = member.value("Priority", "");
14055badf79SAlexander Hansen     std::optional<phosphor::led::Layout::Action> priority = std::nullopt;
14155badf79SAlexander Hansen 
14255badf79SAlexander Hansen     if (!priorityStr.empty())
14355badf79SAlexander Hansen     {
14455badf79SAlexander Hansen         priority = getAction(priorityStr);
14555badf79SAlexander Hansen     }
146d0f80506SAlexander Hansen 
147d0f80506SAlexander Hansen     // Same LEDs can be part of multiple groups. However, their
148d0f80506SAlexander Hansen     // priorities across groups need to match.
149d0f80506SAlexander Hansen     validatePriority(name, priority, priorityMap);
150d0f80506SAlexander Hansen 
151d0f80506SAlexander Hansen     phosphor::led::Layout::LedAction ledAction{name, action, dutyOn, period,
152d0f80506SAlexander Hansen                                                priority};
153d0f80506SAlexander Hansen     ledActions.emplace(ledAction);
154d0f80506SAlexander Hansen }
155d0f80506SAlexander Hansen 
156d0f80506SAlexander Hansen static void loadJsonConfigV1Group(const Json& entry,
157d0f80506SAlexander Hansen                                   phosphor::led::GroupMap& ledMap,
158d0f80506SAlexander Hansen                                   PriorityMap& priorityMap)
159d0f80506SAlexander Hansen {
160d0f80506SAlexander Hansen     const Json empty{};
161d0f80506SAlexander Hansen 
162d0f80506SAlexander Hansen     fs::path tmpPath("/xyz/openbmc_project/led/groups");
163d0f80506SAlexander Hansen 
164d0f80506SAlexander Hansen     const std::string groupName = entry.value("group", "");
165d0f80506SAlexander Hansen 
166d0f80506SAlexander Hansen     tmpPath /= groupName;
167d0f80506SAlexander Hansen     auto objpath = tmpPath.string();
168d0f80506SAlexander Hansen     auto members = entry.value("members", empty);
1697ba70c82SAlexander Hansen     int priority = entry.value("Priority", 0);
170d0f80506SAlexander Hansen 
171d0f80506SAlexander Hansen     lg2::debug("config for '{GROUP}'", "GROUP", groupName);
172d0f80506SAlexander Hansen 
173d0f80506SAlexander Hansen     phosphor::led::ActionSet ledActions{};
1747ba70c82SAlexander Hansen     phosphor::led::Layout::GroupLayout groupLayout{};
175d0f80506SAlexander Hansen     for (const auto& member : members)
176d0f80506SAlexander Hansen     {
177d0f80506SAlexander Hansen         loadJsonConfigV1GroupMember(member, priorityMap, ledActions);
178d0f80506SAlexander Hansen     }
179d0f80506SAlexander Hansen 
180d0f80506SAlexander Hansen     // Generated an std::unordered_map of LedGroupNames to std::set of LEDs
181d0f80506SAlexander Hansen     // containing the name and properties.
1827ba70c82SAlexander Hansen     groupLayout.actionSet = ledActions;
1837ba70c82SAlexander Hansen     groupLayout.priority = priority;
1847ba70c82SAlexander Hansen 
1857ba70c82SAlexander Hansen     ledMap.emplace(objpath, groupLayout);
186d0f80506SAlexander Hansen }
187d0f80506SAlexander Hansen 
18873ff9ad6SPatrick Williams /** @brief Load JSON config and return led map (JSON version 1)
189953315d2SPatrick Williams  *
190158b2c14SPatrick Williams  *  @return phosphor::led::GroupMap
191953315d2SPatrick Williams  */
192158b2c14SPatrick Williams const phosphor::led::GroupMap loadJsonConfigV1(const Json& json)
193953315d2SPatrick Williams {
194158b2c14SPatrick Williams     phosphor::led::GroupMap ledMap{};
195953315d2SPatrick Williams     PriorityMap priorityMap{};
196953315d2SPatrick Williams 
197953315d2SPatrick Williams     // define the default JSON as empty
198953315d2SPatrick Williams     const Json empty{};
199953315d2SPatrick Williams     auto leds = json.value("leds", empty);
200953315d2SPatrick Williams 
201953315d2SPatrick Williams     for (const auto& entry : leds)
202953315d2SPatrick Williams     {
203d0f80506SAlexander Hansen         loadJsonConfigV1Group(entry, ledMap, priorityMap);
204953315d2SPatrick Williams     }
205953315d2SPatrick Williams 
206953315d2SPatrick Williams     return ledMap;
207953315d2SPatrick Williams }
208953315d2SPatrick Williams 
20973ff9ad6SPatrick Williams /** @brief Load JSON config and return led map
21073ff9ad6SPatrick Williams  *
211158b2c14SPatrick Williams  *  @return phosphor::led::GroupMap
21273ff9ad6SPatrick Williams  */
213158b2c14SPatrick Williams const phosphor::led::GroupMap loadJsonConfig(const fs::path& path)
21473ff9ad6SPatrick Williams {
21573ff9ad6SPatrick Williams     auto json = readJson(path);
21673ff9ad6SPatrick Williams 
21773ff9ad6SPatrick Williams     auto version = json.value("version", 1);
21873ff9ad6SPatrick Williams     switch (version)
21973ff9ad6SPatrick Williams     {
22073ff9ad6SPatrick Williams         case 1:
22173ff9ad6SPatrick Williams             return loadJsonConfigV1(json);
22273ff9ad6SPatrick Williams 
22373ff9ad6SPatrick Williams         default:
22473ff9ad6SPatrick Williams             lg2::error("Unsupported JSON Version: {VERSION}", "VERSION",
22573ff9ad6SPatrick Williams                        version);
22673ff9ad6SPatrick Williams             throw std::runtime_error("Unsupported version");
22773ff9ad6SPatrick Williams     }
22873ff9ad6SPatrick Williams 
229158b2c14SPatrick Williams     return phosphor::led::GroupMap{};
23073ff9ad6SPatrick Williams }
23173ff9ad6SPatrick Williams 
232953315d2SPatrick Williams /** @brief Get led map from LED groups JSON config
233953315d2SPatrick Williams  *
2347217c035SPatrick Williams  *  @param[in] config - Path to the JSON config.
235158b2c14SPatrick Williams  *  @return phosphor::led::GroupMap
2367217c035SPatrick Williams  *
2377217c035SPatrick Williams  *  @note if config is an empty string, daemon will interrogate dbus for
2387217c035SPatrick Williams  *        compatible strings.
239953315d2SPatrick Williams  */
240158b2c14SPatrick Williams const phosphor::led::GroupMap getSystemLedMap(fs::path config)
241953315d2SPatrick Williams {
2427217c035SPatrick Williams     if (config.empty())
243953315d2SPatrick Williams     {
2447217c035SPatrick Williams         config = phosphor::led::getJsonConfig();
245953315d2SPatrick Williams     }
246953315d2SPatrick Williams 
2477217c035SPatrick Williams     return loadJsonConfig(config);
248953315d2SPatrick Williams }
249