blob: 245587d530447c90df0899ab558dd3023a81dca9 [file] [log] [blame]
George Liu616a0712021-02-18 10:50:24 +08001#include "config.h"
2
3#include "json-config.hpp"
4#include "ledlayout.hpp"
5
6#include <nlohmann/json.hpp>
7#include <phosphor-logging/log.hpp>
8#include <sdbusplus/bus.hpp>
9#include <sdeventplus/event.hpp>
10
11#include <filesystem>
12#include <fstream>
13#include <iostream>
14
15namespace fs = std::filesystem;
16
17using Json = nlohmann::json;
18using LedAction = std::set<phosphor::led::Layout::LedAction>;
19using LedMap = std::map<std::string, LedAction>;
20
21// Priority for a particular LED needs to stay SAME across all groups
22// phosphor::led::Layout::Action can only be one of `Blink` and `On`
23using PriorityMap = std::map<std::string, phosphor::led::Layout::Action>;
24
25/** @brief Parse LED JSON file and output Json object
26 *
27 * @param[in] path - path of LED JSON file
28 *
29 * @return const Json - Json object
30 */
31const Json readJson(const fs::path& path)
32{
33 using namespace phosphor::logging;
34
35 if (!fs::exists(path) || fs::is_empty(path))
36 {
37 log<level::ERR>("Incorrect File Path or empty file",
38 entry("FILE_PATH=%s", path.c_str()));
39 throw std::runtime_error("Incorrect File Path or empty file");
40 }
41
42 try
43 {
44 std::ifstream jsonFile(path);
45 return Json::parse(jsonFile);
46 }
47 catch (const std::exception& e)
48 {
49 log<level::ERR>("Failed to parse config file",
50 entry("ERROR=%s", e.what()),
51 entry("FILE_PATH=%s", path.c_str()));
52 throw std::runtime_error("Failed to parse config file");
53 }
54}
55
56/** @brief Returns action enum based on string
57 *
58 * @param[in] action - action string
59 *
60 * @return Action - action enum (On/Blink)
61 */
62phosphor::led::Layout::Action getAction(const std::string& action)
63{
64 assert(action == "On" || action == "Blink");
65
66 return action == "Blink" ? phosphor::led::Layout::Blink
67 : phosphor::led::Layout::On;
68}
69
70/** @brief Validate the Priority of an LED is same across ALL groups
71 *
72 * @param[in] name - led name member of each group
73 * @param[in] priority - member priority of each group
74 * @param[out] priorityMap - std::map, key:name, value:priority
75 *
76 * @return
77 */
78void validatePriority(const std::string& name,
79 const phosphor::led::Layout::Action& priority,
80 PriorityMap& priorityMap)
81{
82 using namespace phosphor::logging;
83
84 auto iter = priorityMap.find(name);
85 if (iter == priorityMap.end())
86 {
87 priorityMap.emplace(name, priority);
88 return;
89 }
90
91 if (iter->second != priority)
92 {
93 log<level::ERR>("Priority of LED is not same across all",
94 entry("Name=%s", name.c_str()),
95 entry(" Old Priority=%d", iter->second),
96 entry(" New priority=%d", priority));
97
98 throw std::runtime_error(
99 "Priority of at least one LED is not same across groups");
100 }
101}
102
103/** @brief Load JSON config and return led map
104 *
105 * @return LedMap - Generated an std::map of LedAction
106 */
107const LedMap loadJsonConfig(const fs::path& path)
108{
109 LedMap ledMap{};
110 PriorityMap priorityMap{};
111
112 // define the default JSON as empty
113 const Json empty{};
114 auto json = readJson(path);
115 auto leds = json.value("leds", empty);
116
117 for (const auto& entry : leds)
118 {
119 fs::path tmpPath(std::string{OBJPATH});
120 tmpPath /= entry.value("group", "");
121 auto objpath = tmpPath.string();
122 auto members = entry.value("members", empty);
123
124 LedAction ledActions{};
125 for (const auto& member : members)
126 {
127 auto name = member.value("Name", "");
128 auto action = getAction(member.value("Action", ""));
129 uint8_t dutyOn = member.value("DutyOn", 50);
130 uint16_t period = member.value("Period", 0);
131
132 // Since only have Blink/On and default priority is Blink
133 auto priority = getAction(member.value("Priority", "Blink"));
134
135 // Same LEDs can be part of multiple groups. However, their
136 // priorities across groups need to match.
137 validatePriority(name, priority, priorityMap);
138
139 phosphor::led::Layout::LedAction ledAction{name, action, dutyOn,
140 period, priority};
141 ledActions.emplace(ledAction);
142 }
143
144 // Generated an std::map of LedGroupNames to std::set of LEDs
145 // containing the name and properties.
146 ledMap.emplace(objpath, ledActions);
147 }
148
149 return ledMap;
150}
151
152/** @brief Get led map from LED groups JSON config
153 *
154 * @return LedMap - Generated an std::map of LedAction
155 */
156const LedMap getSystemLedMap()
157{
158 // Get a new Dbus
159 auto bus = sdbusplus::bus::new_bus();
160
161 // Get a new event loop
162 auto event = sdeventplus::Event::get_new();
163
164 // Attach the bus to sd_event to service user requests
165 bus.attach_event(event.get(), SD_EVENT_PRIORITY_IMPORTANT);
166 phosphor::led::JsonConfig jsonConfig(bus, event);
167
168 // The event loop will be terminated from inside of a function in JsonConfig
169 // after finding the configuration file
170 if (jsonConfig.getConfFile().empty())
171 {
172 event.loop();
173 }
174
175 // Detach the bus from its sd_event event loop object
176 bus.detach_event();
177
178 return loadJsonConfig(jsonConfig.getConfFile());
179}