xref: /openbmc/phosphor-led-manager/manager/lamptest/lamptest.cpp (revision 7217c0354245f9096adb4301945951e030d322c8)
1 #include "lamptest.hpp"
2 
3 #include <phosphor-logging/lg2.hpp>
4 
5 #include <algorithm>
6 
7 namespace phosphor
8 {
9 namespace led
10 {
11 
12 using Json = nlohmann::json;
13 
14 bool LampTest::processLEDUpdates(const Manager::group& ledsAssert,
15                                  const Manager::group& ledsDeAssert)
16 {
17     // If the physical LED status is updated during the lamp test, it should be
18     // saved to Queue, and the queue will be processed after the lamp test is
19     // stopped.
20     if (isLampTestRunning)
21     {
22         // Physical LEDs will be updated during lamp test
23         for (const auto& it : ledsDeAssert)
24         {
25             std::string path = std::string(PHY_LED_PATH) + it.name;
26             auto iter = std::find_if(
27                 forceUpdateLEDs.begin(), forceUpdateLEDs.end(),
28                 [&path](const auto& name) { return name == path; });
29 
30             if (iter != forceUpdateLEDs.end())
31             {
32                 manager.drivePhysicalLED(path, Layout::Action::Off, it.dutyOn,
33                                          it.period);
34             }
35         }
36 
37         for (const auto& it : ledsAssert)
38         {
39             std::string path = std::string(PHY_LED_PATH) + it.name;
40             auto iter = std::find_if(
41                 forceUpdateLEDs.begin(), forceUpdateLEDs.end(),
42                 [&path](const auto& name) { return name == path; });
43 
44             if (iter != forceUpdateLEDs.end())
45             {
46                 manager.drivePhysicalLED(path, it.action, it.dutyOn, it.period);
47             }
48         }
49 
50         updatedLEDsDuringLampTest.emplace(
51             std::make_pair(ledsAssert, ledsDeAssert));
52         return true;
53     }
54     return false;
55 }
56 
57 void LampTest::stop()
58 {
59     if (!isLampTestRunning)
60     {
61         return;
62     }
63 
64     timer.setEnabled(false);
65 
66     // Stop host lamp test
67     doHostLampTest(false);
68 
69     // Set all the Physical action to Off
70     for (const auto& path : physicalLEDPaths)
71     {
72         auto iter =
73             std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
74                          [&path](const auto& skip) { return skip == path; });
75 
76         if (iter != skipUpdateLEDs.end())
77         {
78             // Skip update physical path
79             continue;
80         }
81 
82         manager.drivePhysicalLED(path, Layout::Action::Off, 0, 0);
83     }
84 
85     isLampTestRunning = false;
86     restorePhysicalLedStates();
87 }
88 
89 Layout::Action LampTest::getActionFromString(const std::string& str)
90 {
91     Layout::Action action = Layout::Off;
92 
93     if (str == "xyz.openbmc_project.Led.Physical.Action.On")
94     {
95         action = Layout::On;
96     }
97     else if (str == "xyz.openbmc_project.Led.Physical.Action.Blink")
98     {
99         action = Layout::Blink;
100     }
101 
102     return action;
103 }
104 
105 void LampTest::storePhysicalLEDsStates()
106 {
107     physicalLEDStatesPriorToLampTest.clear();
108 
109     for (const auto& path : physicalLEDPaths)
110     {
111         auto iter = std::find_if(
112             skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
113             [&path](const auto& skipLed) { return skipLed == path; });
114 
115         if (iter != skipUpdateLEDs.end())
116         {
117             // Physical LEDs will be skipped
118             continue;
119         }
120 
121         // Reverse intercept path, Get the name of each member of physical led
122         // e.g: path = /xyz/openbmc_project/led/physical/front_fan
123         //      name = front_fan
124         sdbusplus::message::object_path object_path(path);
125         auto name = object_path.filename();
126         if (name.empty())
127         {
128             lg2::error(
129                 "Failed to get the name of member of physical LED path, PATH = {PATH}, NAME = {NAME}",
130                 "PATH", path, "NAME", name);
131             continue;
132         }
133 
134         std::string state{};
135         uint16_t period{};
136         uint8_t dutyOn{};
137         try
138         {
139             auto properties = dBusHandler.getAllProperties(path, PHY_LED_IFACE);
140 
141             state = std::get<std::string>(properties["State"]);
142             period = std::get<uint16_t>(properties["Period"]);
143             dutyOn = std::get<uint8_t>(properties["DutyOn"]);
144         }
145         catch (const sdbusplus::exception::exception& e)
146         {
147             lg2::error(
148                 "Failed to get All properties, ERROR = {ERROR}, PATH = {PATH}",
149                 "ERROR", e, "PATH", path);
150             continue;
151         }
152 
153         phosphor::led::Layout::Action action = getActionFromString(state);
154         if (action != phosphor::led::Layout::Off)
155         {
156             phosphor::led::Layout::LedAction ledAction{
157                 name, action, dutyOn, period, phosphor::led::Layout::On};
158             physicalLEDStatesPriorToLampTest.emplace(ledAction);
159         }
160     }
161 }
162 
163 void LampTest::start()
164 {
165     if (isLampTestRunning)
166     {
167         // reset the timer and then return
168         timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
169         return;
170     }
171 
172     // Get paths of all the Physical LED objects
173     physicalLEDPaths = dBusHandler.getSubTreePaths(PHY_LED_PATH, PHY_LED_IFACE);
174 
175     // Get physical LEDs states before lamp test
176     storePhysicalLEDsStates();
177 
178     // restart lamp test, it contains initiate or reset the timer.
179     timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
180     isLampTestRunning = true;
181 
182     // Notify PHYP to start the lamp test
183     doHostLampTest(true);
184 
185     // Set all the Physical action to On for lamp test
186     for (const auto& path : physicalLEDPaths)
187     {
188         auto iter =
189             std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
190                          [&path](const auto& skip) { return skip == path; });
191 
192         if (iter != skipUpdateLEDs.end())
193         {
194             // Skip update physical path
195             continue;
196         }
197 
198         manager.drivePhysicalLED(path, Layout::Action::On, 0, 0);
199     }
200 }
201 
202 void LampTest::timeOutHandler()
203 {
204     // set the Asserted property of lamp test to false
205     if (!groupObj)
206     {
207         lg2::error("the Group object is nullptr");
208         throw std::runtime_error("the Group object is nullptr");
209     }
210 
211     groupObj->asserted(false);
212 }
213 
214 void LampTest::requestHandler(Group* group, bool value)
215 {
216     if (groupObj == NULL)
217     {
218         groupObj = std::move(group);
219     }
220 
221     if (value)
222     {
223         start();
224     }
225     else
226     {
227         stop();
228     }
229 }
230 
231 void LampTest::restorePhysicalLedStates()
232 {
233     // restore physical LEDs states before lamp test
234     Manager::group ledsDeAssert{};
235     manager.driveLEDs(physicalLEDStatesPriorToLampTest, ledsDeAssert);
236     physicalLEDStatesPriorToLampTest.clear();
237 
238     // restore physical LEDs states during lamp test
239     while (!updatedLEDsDuringLampTest.empty())
240     {
241         auto& [ledsAssert, ledsDeAssert] = updatedLEDsDuringLampTest.front();
242         manager.driveLEDs(ledsAssert, ledsDeAssert);
243         updatedLEDsDuringLampTest.pop();
244     }
245 }
246 
247 void LampTest::doHostLampTest(bool value)
248 {
249     try
250     {
251         PropertyValue assertedValue{value};
252         dBusHandler.setProperty(HOST_LAMP_TEST_OBJECT,
253                                 "xyz.openbmc_project.Led.Group", "Asserted",
254                                 assertedValue);
255     }
256     catch (const sdbusplus::exception::exception& e)
257     {
258         lg2::error(
259             "Failed to set Asserted property, ERROR = {ERROR}, PATH = {PATH}",
260             "ERROR", e, "PATH", std::string(HOST_LAMP_TEST_OBJECT));
261     }
262 }
263 
264 void LampTest::getPhysicalLEDNamesFromJson(const fs::path& path)
265 {
266     if (!fs::exists(path) || fs::is_empty(path))
267     {
268         lg2::info("The file does not exist or is empty, FILE_PATH = {PATH}",
269                   "PATH", path);
270         return;
271     }
272 
273     try
274     {
275         std::ifstream jsonFile(path);
276         auto json = Json::parse(jsonFile);
277 
278         // define the default JSON as empty
279         const std::vector<std::string> empty{};
280         auto forceLEDs = json.value("forceLEDs", empty);
281         std::ranges::transform(forceLEDs, std::back_inserter(forceUpdateLEDs),
282                                [](const auto& i) { return PHY_LED_PATH + i; });
283 
284         auto skipLEDs = json.value("skipLEDs", empty);
285         std::ranges::transform(skipLEDs, std::back_inserter(skipUpdateLEDs),
286                                [](const auto& i) { return PHY_LED_PATH + i; });
287     }
288     catch (const std::exception& e)
289     {
290         lg2::error(
291             "Failed to parse config file, ERROR = {ERROR}, FILE_PATH = {PATH}",
292             "ERROR", e, "PATH", path);
293     }
294     return;
295 }
296 
297 } // namespace led
298 } // namespace phosphor
299