xref: /openbmc/phosphor-led-manager/manager/lamptest/lamptest.cpp (revision 49875a26a8a8510ee281bbf0e6828b618b66c4eb)
1953315d2SPatrick Williams #include "lamptest.hpp"
2953315d2SPatrick Williams 
3953315d2SPatrick Williams #include <phosphor-logging/lg2.hpp>
4953315d2SPatrick Williams 
5953315d2SPatrick Williams #include <algorithm>
6953315d2SPatrick Williams 
7953315d2SPatrick Williams namespace phosphor
8953315d2SPatrick Williams {
9953315d2SPatrick Williams namespace led
10953315d2SPatrick Williams {
11953315d2SPatrick Williams 
12953315d2SPatrick Williams using Json = nlohmann::json;
13e3515c71SSunny Srivastava static const fs::path lampTestIndicator =
14e3515c71SSunny Srivastava     "/var/lib/phosphor-led-manager/lamp-test-running";
15953315d2SPatrick Williams 
processLEDUpdates(const ActionSet & ledsAssert,const ActionSet & ledsDeAssert)16158b2c14SPatrick Williams bool LampTest::processLEDUpdates(const ActionSet& ledsAssert,
17158b2c14SPatrick Williams                                  const ActionSet& ledsDeAssert)
18953315d2SPatrick Williams {
19953315d2SPatrick Williams     // If the physical LED status is updated during the lamp test, it should be
20953315d2SPatrick Williams     // saved to Queue, and the queue will be processed after the lamp test is
21953315d2SPatrick Williams     // stopped.
22953315d2SPatrick Williams     if (isLampTestRunning)
23953315d2SPatrick Williams     {
24953315d2SPatrick Williams         // Physical LEDs will be updated during lamp test
25953315d2SPatrick Williams         for (const auto& it : ledsDeAssert)
26953315d2SPatrick Williams         {
271f0b715aSGeorge Liu             std::string path = std::string(phyLedPath) + it.name;
28953315d2SPatrick Williams             auto iter = std::find_if(
29953315d2SPatrick Williams                 forceUpdateLEDs.begin(), forceUpdateLEDs.end(),
30953315d2SPatrick Williams                 [&path](const auto& name) { return name == path; });
31953315d2SPatrick Williams 
32953315d2SPatrick Williams             if (iter != forceUpdateLEDs.end())
33953315d2SPatrick Williams             {
34f0592559SGeorge Liu                 phosphor::led::Manager::drivePhysicalLED(
35f0592559SGeorge Liu                     path, Layout::Action::Off, it.dutyOn, it.period);
36953315d2SPatrick Williams             }
37953315d2SPatrick Williams         }
38953315d2SPatrick Williams 
39953315d2SPatrick Williams         for (const auto& it : ledsAssert)
40953315d2SPatrick Williams         {
411f0b715aSGeorge Liu             std::string path = std::string(phyLedPath) + it.name;
42953315d2SPatrick Williams             auto iter = std::find_if(
43953315d2SPatrick Williams                 forceUpdateLEDs.begin(), forceUpdateLEDs.end(),
44953315d2SPatrick Williams                 [&path](const auto& name) { return name == path; });
45953315d2SPatrick Williams 
46953315d2SPatrick Williams             if (iter != forceUpdateLEDs.end())
47953315d2SPatrick Williams             {
48f0592559SGeorge Liu                 phosphor::led::Manager::drivePhysicalLED(path, it.action,
49f0592559SGeorge Liu                                                          it.dutyOn, it.period);
50953315d2SPatrick Williams             }
51953315d2SPatrick Williams         }
52953315d2SPatrick Williams 
53fcf08106SGeorge Liu         updatedLEDsDuringLampTest.emplace(ledsAssert, ledsDeAssert);
54953315d2SPatrick Williams         return true;
55953315d2SPatrick Williams     }
56953315d2SPatrick Williams     return false;
57953315d2SPatrick Williams }
58953315d2SPatrick Williams 
stop()59953315d2SPatrick Williams void LampTest::stop()
60953315d2SPatrick Williams {
61953315d2SPatrick Williams     if (!isLampTestRunning)
62953315d2SPatrick Williams     {
63953315d2SPatrick Williams         return;
64953315d2SPatrick Williams     }
65953315d2SPatrick Williams 
66953315d2SPatrick Williams     timer.setEnabled(false);
67953315d2SPatrick Williams 
68953315d2SPatrick Williams     // Stop host lamp test
69953315d2SPatrick Williams     doHostLampTest(false);
70953315d2SPatrick Williams 
71953315d2SPatrick Williams     // Set all the Physical action to Off
72953315d2SPatrick Williams     for (const auto& path : physicalLEDPaths)
73953315d2SPatrick Williams     {
74953315d2SPatrick Williams         auto iter =
75953315d2SPatrick Williams             std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
76953315d2SPatrick Williams                          [&path](const auto& skip) { return skip == path; });
77953315d2SPatrick Williams 
78953315d2SPatrick Williams         if (iter != skipUpdateLEDs.end())
79953315d2SPatrick Williams         {
80953315d2SPatrick Williams             // Skip update physical path
81953315d2SPatrick Williams             continue;
82953315d2SPatrick Williams         }
83953315d2SPatrick Williams 
84f0592559SGeorge Liu         phosphor::led::Manager::drivePhysicalLED(path, Layout::Action::Off, 0,
85f0592559SGeorge Liu                                                  0);
86953315d2SPatrick Williams     }
87953315d2SPatrick Williams 
88e3515c71SSunny Srivastava     if (std::filesystem::exists(lampTestIndicator))
89e3515c71SSunny Srivastava     {
90e3515c71SSunny Srivastava         if (!std::filesystem::remove(lampTestIndicator))
91e3515c71SSunny Srivastava         {
92e3515c71SSunny Srivastava             lg2::error(
93e3515c71SSunny Srivastava                 "Error removing lamp test on indicator file after lamp test execution.");
94e3515c71SSunny Srivastava         }
95e3515c71SSunny Srivastava     }
96e3515c71SSunny Srivastava 
97953315d2SPatrick Williams     isLampTestRunning = false;
98953315d2SPatrick Williams     restorePhysicalLedStates();
99953315d2SPatrick Williams }
100953315d2SPatrick Williams 
getActionFromString(const std::string & str)101953315d2SPatrick Williams Layout::Action LampTest::getActionFromString(const std::string& str)
102953315d2SPatrick Williams {
103ed80e885SPatrick Williams     Layout::Action action = Layout::Action::Off;
104953315d2SPatrick Williams 
105953315d2SPatrick Williams     if (str == "xyz.openbmc_project.Led.Physical.Action.On")
106953315d2SPatrick Williams     {
107ed80e885SPatrick Williams         action = Layout::Action::On;
108953315d2SPatrick Williams     }
109953315d2SPatrick Williams     else if (str == "xyz.openbmc_project.Led.Physical.Action.Blink")
110953315d2SPatrick Williams     {
111ed80e885SPatrick Williams         action = Layout::Action::Blink;
112953315d2SPatrick Williams     }
113953315d2SPatrick Williams 
114953315d2SPatrick Williams     return action;
115953315d2SPatrick Williams }
116953315d2SPatrick Williams 
storePhysicalLEDsStates()117953315d2SPatrick Williams void LampTest::storePhysicalLEDsStates()
118953315d2SPatrick Williams {
119953315d2SPatrick Williams     physicalLEDStatesPriorToLampTest.clear();
120953315d2SPatrick Williams 
121953315d2SPatrick Williams     for (const auto& path : physicalLEDPaths)
122953315d2SPatrick Williams     {
123605600ebSPatrick Williams         auto iter = std::find_if(
124605600ebSPatrick Williams             skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
125605600ebSPatrick Williams             [&path](const auto& skipLed) { return skipLed == path; });
126953315d2SPatrick Williams 
127953315d2SPatrick Williams         if (iter != skipUpdateLEDs.end())
128953315d2SPatrick Williams         {
129953315d2SPatrick Williams             // Physical LEDs will be skipped
130953315d2SPatrick Williams             continue;
131953315d2SPatrick Williams         }
132953315d2SPatrick Williams 
133953315d2SPatrick Williams         // Reverse intercept path, Get the name of each member of physical led
134953315d2SPatrick Williams         // e.g: path = /xyz/openbmc_project/led/physical/front_fan
135953315d2SPatrick Williams         //      name = front_fan
136953315d2SPatrick Williams         sdbusplus::message::object_path object_path(path);
137953315d2SPatrick Williams         auto name = object_path.filename();
138953315d2SPatrick Williams         if (name.empty())
139953315d2SPatrick Williams         {
140953315d2SPatrick Williams             lg2::error(
141953315d2SPatrick Williams                 "Failed to get the name of member of physical LED path, PATH = {PATH}, NAME = {NAME}",
142953315d2SPatrick Williams                 "PATH", path, "NAME", name);
143953315d2SPatrick Williams             continue;
144953315d2SPatrick Williams         }
145953315d2SPatrick Williams 
146953315d2SPatrick Williams         std::string state{};
147953315d2SPatrick Williams         uint16_t period{};
148953315d2SPatrick Williams         uint8_t dutyOn{};
149953315d2SPatrick Williams         try
150953315d2SPatrick Williams         {
151f0592559SGeorge Liu             auto properties =
152f0592559SGeorge Liu                 phosphor::led::utils::DBusHandler::getAllProperties(path,
153f0592559SGeorge Liu                                                                     phyLedIntf);
154953315d2SPatrick Williams 
155953315d2SPatrick Williams             state = std::get<std::string>(properties["State"]);
156953315d2SPatrick Williams             period = std::get<uint16_t>(properties["Period"]);
157953315d2SPatrick Williams             dutyOn = std::get<uint8_t>(properties["DutyOn"]);
158953315d2SPatrick Williams         }
1593e073ba6SPatrick Williams         catch (const sdbusplus::exception_t& e)
160953315d2SPatrick Williams         {
161953315d2SPatrick Williams             lg2::error(
162953315d2SPatrick Williams                 "Failed to get All properties, ERROR = {ERROR}, PATH = {PATH}",
163953315d2SPatrick Williams                 "ERROR", e, "PATH", path);
164953315d2SPatrick Williams             continue;
165953315d2SPatrick Williams         }
166953315d2SPatrick Williams 
167953315d2SPatrick Williams         phosphor::led::Layout::Action action = getActionFromString(state);
168ed80e885SPatrick Williams         if (action != phosphor::led::Layout::Action::Off)
169953315d2SPatrick Williams         {
170953315d2SPatrick Williams             phosphor::led::Layout::LedAction ledAction{
171ed80e885SPatrick Williams                 name, action, dutyOn, period,
172ed80e885SPatrick Williams                 phosphor::led::Layout::Action::On};
173953315d2SPatrick Williams             physicalLEDStatesPriorToLampTest.emplace(ledAction);
174953315d2SPatrick Williams         }
175953315d2SPatrick Williams     }
176953315d2SPatrick Williams }
177953315d2SPatrick Williams 
start()178953315d2SPatrick Williams void LampTest::start()
179953315d2SPatrick Williams {
180953315d2SPatrick Williams     if (isLampTestRunning)
181953315d2SPatrick Williams     {
182953315d2SPatrick Williams         // reset the timer and then return
183953315d2SPatrick Williams         timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
184180090cfSPriyangaRamasamy 
185180090cfSPriyangaRamasamy         // Notify host to reset the timer
186180090cfSPriyangaRamasamy         doHostLampTest(true);
187180090cfSPriyangaRamasamy 
188953315d2SPatrick Williams         return;
189953315d2SPatrick Williams     }
190953315d2SPatrick Williams 
191953315d2SPatrick Williams     // Get paths of all the Physical LED objects
192785f5054SGeorge Liu     try
193785f5054SGeorge Liu     {
194f0592559SGeorge Liu         physicalLEDPaths = phosphor::led::utils::DBusHandler::getSubTreePaths(
195f0592559SGeorge Liu             phyLedPath, phyLedIntf);
196785f5054SGeorge Liu     }
197785f5054SGeorge Liu     catch (const sdbusplus::exception_t& e)
198785f5054SGeorge Liu     {
199785f5054SGeorge Liu         lg2::error(
200785f5054SGeorge Liu             "Failed to call the SubTreePaths method: {ERROR}, ledPath: {PATH}, ledInterface: {INTERFACE}",
2011f0b715aSGeorge Liu             "ERROR", e, "PATH", phyLedPath, "INTERFACE", phyLedIntf);
202785f5054SGeorge Liu         return;
203785f5054SGeorge Liu     }
204953315d2SPatrick Williams 
205953315d2SPatrick Williams     // Get physical LEDs states before lamp test
206953315d2SPatrick Williams     storePhysicalLEDsStates();
207953315d2SPatrick Williams 
208953315d2SPatrick Williams     // restart lamp test, it contains initiate or reset the timer.
209953315d2SPatrick Williams     timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
210953315d2SPatrick Williams     isLampTestRunning = true;
211953315d2SPatrick Williams 
212180090cfSPriyangaRamasamy     // Notify host to start the lamp test
213953315d2SPatrick Williams     doHostLampTest(true);
214953315d2SPatrick Williams 
215e3515c71SSunny Srivastava     // Create a file to maintain the state across reboots that Lamp test is on.
216e3515c71SSunny Srivastava     // This is required as there was a scenario where it has been found that
217e3515c71SSunny Srivastava     // LEDs remains in "on" state if lamp test is triggered and reboot takes
218e3515c71SSunny Srivastava     // place.
219e3515c71SSunny Srivastava     const auto ledDirectory = lampTestIndicator.parent_path();
220e3515c71SSunny Srivastava 
221e3515c71SSunny Srivastava     if (!fs::exists(ledDirectory))
222e3515c71SSunny Srivastava     {
223e3515c71SSunny Srivastava         fs::create_directories(ledDirectory);
224e3515c71SSunny Srivastava     }
225e3515c71SSunny Srivastava 
2268f538d9cSGeorge Liu     std::ofstream ofs(lampTestIndicator.c_str());
227e3515c71SSunny Srivastava 
228953315d2SPatrick Williams     // Set all the Physical action to On for lamp test
229953315d2SPatrick Williams     for (const auto& path : physicalLEDPaths)
230953315d2SPatrick Williams     {
231953315d2SPatrick Williams         auto iter =
232953315d2SPatrick Williams             std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
233953315d2SPatrick Williams                          [&path](const auto& skip) { return skip == path; });
234953315d2SPatrick Williams 
235953315d2SPatrick Williams         if (iter != skipUpdateLEDs.end())
236953315d2SPatrick Williams         {
237953315d2SPatrick Williams             // Skip update physical path
238953315d2SPatrick Williams             continue;
239953315d2SPatrick Williams         }
240953315d2SPatrick Williams 
241f0592559SGeorge Liu         phosphor::led::Manager::drivePhysicalLED(path, Layout::Action::On, 0,
242f0592559SGeorge Liu                                                  0);
243953315d2SPatrick Williams     }
244953315d2SPatrick Williams }
245953315d2SPatrick Williams 
timeOutHandler()246953315d2SPatrick Williams void LampTest::timeOutHandler()
247953315d2SPatrick Williams {
248953315d2SPatrick Williams     // set the Asserted property of lamp test to false
249*49875a26SGeorge Liu     if (groupObj == nullptr)
250953315d2SPatrick Williams     {
251953315d2SPatrick Williams         lg2::error("the Group object is nullptr");
252953315d2SPatrick Williams         throw std::runtime_error("the Group object is nullptr");
253953315d2SPatrick Williams     }
254953315d2SPatrick Williams 
255953315d2SPatrick Williams     groupObj->asserted(false);
256953315d2SPatrick Williams }
257953315d2SPatrick Williams 
requestHandler(Group * group,bool value)258180090cfSPriyangaRamasamy bool LampTest::requestHandler(Group* group, bool value)
259953315d2SPatrick Williams {
260aaa667f2SGeorge Liu     if (groupObj == nullptr)
261953315d2SPatrick Williams     {
262df3ab7c9SGeorge Liu         groupObj = group;
263953315d2SPatrick Williams     }
264953315d2SPatrick Williams 
265953315d2SPatrick Williams     if (value)
266953315d2SPatrick Williams     {
267953315d2SPatrick Williams         start();
268180090cfSPriyangaRamasamy 
269180090cfSPriyangaRamasamy         // Return true in both cases (F -> T && T -> T)
270180090cfSPriyangaRamasamy         return true;
271953315d2SPatrick Williams     }
272953315d2SPatrick Williams     else
273953315d2SPatrick Williams     {
274180090cfSPriyangaRamasamy         if (timer.hasExpired())
275180090cfSPriyangaRamasamy         {
276953315d2SPatrick Williams             stop();
277180090cfSPriyangaRamasamy 
278180090cfSPriyangaRamasamy             // Return true as the request to stop the lamptest is handled
279180090cfSPriyangaRamasamy             // successfully.
280180090cfSPriyangaRamasamy             return true;
281180090cfSPriyangaRamasamy         }
282180090cfSPriyangaRamasamy         else if (timer.isEnabled())
283180090cfSPriyangaRamasamy         {
284180090cfSPriyangaRamasamy             lg2::info(
285180090cfSPriyangaRamasamy                 "Lamp test is still running. Cannot force stop the lamp test. Asserted is set back to true.");
286180090cfSPriyangaRamasamy 
287180090cfSPriyangaRamasamy             // Return false as the request to stop lamptest is not handled as
288180090cfSPriyangaRamasamy             // the lamptest is still running.
289180090cfSPriyangaRamasamy             return false;
290180090cfSPriyangaRamasamy         }
291180090cfSPriyangaRamasamy         return false;
292953315d2SPatrick Williams     }
293953315d2SPatrick Williams }
294953315d2SPatrick Williams 
restorePhysicalLedStates()295953315d2SPatrick Williams void LampTest::restorePhysicalLedStates()
296953315d2SPatrick Williams {
297953315d2SPatrick Williams     // restore physical LEDs states before lamp test
298158b2c14SPatrick Williams     ActionSet ledsDeAssert{};
299953315d2SPatrick Williams     manager.driveLEDs(physicalLEDStatesPriorToLampTest, ledsDeAssert);
300953315d2SPatrick Williams     physicalLEDStatesPriorToLampTest.clear();
301953315d2SPatrick Williams 
302953315d2SPatrick Williams     // restore physical LEDs states during lamp test
303953315d2SPatrick Williams     while (!updatedLEDsDuringLampTest.empty())
304953315d2SPatrick Williams     {
305953315d2SPatrick Williams         auto& [ledsAssert, ledsDeAssert] = updatedLEDsDuringLampTest.front();
306953315d2SPatrick Williams         manager.driveLEDs(ledsAssert, ledsDeAssert);
307953315d2SPatrick Williams         updatedLEDsDuringLampTest.pop();
308953315d2SPatrick Williams     }
309953315d2SPatrick Williams }
310953315d2SPatrick Williams 
doHostLampTest(bool value)311953315d2SPatrick Williams void LampTest::doHostLampTest(bool value)
312953315d2SPatrick Williams {
313953315d2SPatrick Williams     try
314953315d2SPatrick Williams     {
315953315d2SPatrick Williams         PropertyValue assertedValue{value};
316f0592559SGeorge Liu         phosphor::led::utils::DBusHandler::setProperty(
317f0592559SGeorge Liu             HOST_LAMP_TEST_OBJECT, "xyz.openbmc_project.Led.Group", "Asserted",
318953315d2SPatrick Williams             assertedValue);
319953315d2SPatrick Williams     }
3203e073ba6SPatrick Williams     catch (const sdbusplus::exception_t& e)
321953315d2SPatrick Williams     {
322953315d2SPatrick Williams         lg2::error(
323953315d2SPatrick Williams             "Failed to set Asserted property, ERROR = {ERROR}, PATH = {PATH}",
324953315d2SPatrick Williams             "ERROR", e, "PATH", std::string(HOST_LAMP_TEST_OBJECT));
325953315d2SPatrick Williams     }
326953315d2SPatrick Williams }
327953315d2SPatrick Williams 
getPhysicalLEDNamesFromJson(const fs::path & path)328953315d2SPatrick Williams void LampTest::getPhysicalLEDNamesFromJson(const fs::path& path)
329953315d2SPatrick Williams {
330953315d2SPatrick Williams     if (!fs::exists(path) || fs::is_empty(path))
331953315d2SPatrick Williams     {
332953315d2SPatrick Williams         lg2::info("The file does not exist or is empty, FILE_PATH = {PATH}",
333953315d2SPatrick Williams                   "PATH", path);
334953315d2SPatrick Williams         return;
335953315d2SPatrick Williams     }
336953315d2SPatrick Williams 
337953315d2SPatrick Williams     try
338953315d2SPatrick Williams     {
339953315d2SPatrick Williams         std::ifstream jsonFile(path);
340953315d2SPatrick Williams         auto json = Json::parse(jsonFile);
341953315d2SPatrick Williams 
342953315d2SPatrick Williams         // define the default JSON as empty
343953315d2SPatrick Williams         const std::vector<std::string> empty{};
344953315d2SPatrick Williams         auto forceLEDs = json.value("forceLEDs", empty);
345953315d2SPatrick Williams         std::ranges::transform(forceLEDs, std::back_inserter(forceUpdateLEDs),
3461f0b715aSGeorge Liu                                [](const auto& i) { return phyLedPath + i; });
347953315d2SPatrick Williams 
348953315d2SPatrick Williams         auto skipLEDs = json.value("skipLEDs", empty);
349953315d2SPatrick Williams         std::ranges::transform(skipLEDs, std::back_inserter(skipUpdateLEDs),
3501f0b715aSGeorge Liu                                [](const auto& i) { return phyLedPath + i; });
351953315d2SPatrick Williams     }
352953315d2SPatrick Williams     catch (const std::exception& e)
353953315d2SPatrick Williams     {
354953315d2SPatrick Williams         lg2::error(
355953315d2SPatrick Williams             "Failed to parse config file, ERROR = {ERROR}, FILE_PATH = {PATH}",
356953315d2SPatrick Williams             "ERROR", e, "PATH", path);
357953315d2SPatrick Williams     }
358953315d2SPatrick Williams     return;
359953315d2SPatrick Williams }
360e3515c71SSunny Srivastava 
clearLamps()361e3515c71SSunny Srivastava void LampTest::clearLamps()
362e3515c71SSunny Srivastava {
363e3515c71SSunny Srivastava     if (std::filesystem::exists(lampTestIndicator))
364e3515c71SSunny Srivastava     {
365e3515c71SSunny Srivastava         // we need to off all the LEDs.
366f0592559SGeorge Liu         std::vector<std::string> physicalLedPaths =
367f0592559SGeorge Liu             phosphor::led::utils::DBusHandler::getSubTreePaths(
368e3515c71SSunny Srivastava                 phosphor::led::phyLedPath, phosphor::led::phyLedIntf);
369e3515c71SSunny Srivastava 
370e3515c71SSunny Srivastava         for (const auto& path : physicalLedPaths)
371e3515c71SSunny Srivastava         {
372f0592559SGeorge Liu             phosphor::led::Manager::drivePhysicalLED(
373f0592559SGeorge Liu                 path, phosphor::led::Layout::Action::Off, 0, 0);
374e3515c71SSunny Srivastava         }
375e3515c71SSunny Srivastava 
376e3515c71SSunny Srivastava         // Also remove the lamp test on indicator file.
377e3515c71SSunny Srivastava         if (!std::filesystem::remove(lampTestIndicator))
378e3515c71SSunny Srivastava         {
379e3515c71SSunny Srivastava             lg2::error(
380e3515c71SSunny Srivastava                 "Error removing lamp test on indicator file after lamp test execution.");
381e3515c71SSunny Srivastava         }
382e3515c71SSunny Srivastava     }
383e3515c71SSunny Srivastava }
384953315d2SPatrick Williams } // namespace led
385953315d2SPatrick Williams } // namespace phosphor
386