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 ActionSet& ledsAssert, 15 const ActionSet& 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::Action::Off; 92 93 if (str == "xyz.openbmc_project.Led.Physical.Action.On") 94 { 95 action = Layout::Action::On; 96 } 97 else if (str == "xyz.openbmc_project.Led.Physical.Action.Blink") 98 { 99 action = Layout::Action::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(skipUpdateLEDs.begin(), skipUpdateLEDs.end(), 112 [&path](const auto& skipLed) { 113 return skipLed == path; 114 }); 115 116 if (iter != skipUpdateLEDs.end()) 117 { 118 // Physical LEDs will be skipped 119 continue; 120 } 121 122 // Reverse intercept path, Get the name of each member of physical led 123 // e.g: path = /xyz/openbmc_project/led/physical/front_fan 124 // name = front_fan 125 sdbusplus::message::object_path object_path(path); 126 auto name = object_path.filename(); 127 if (name.empty()) 128 { 129 lg2::error( 130 "Failed to get the name of member of physical LED path, PATH = {PATH}, NAME = {NAME}", 131 "PATH", path, "NAME", name); 132 continue; 133 } 134 135 std::string state{}; 136 uint16_t period{}; 137 uint8_t dutyOn{}; 138 try 139 { 140 auto properties = dBusHandler.getAllProperties(path, PHY_LED_IFACE); 141 142 state = std::get<std::string>(properties["State"]); 143 period = std::get<uint16_t>(properties["Period"]); 144 dutyOn = std::get<uint8_t>(properties["DutyOn"]); 145 } 146 catch (const sdbusplus::exception_t& e) 147 { 148 lg2::error( 149 "Failed to get All properties, ERROR = {ERROR}, PATH = {PATH}", 150 "ERROR", e, "PATH", path); 151 continue; 152 } 153 154 phosphor::led::Layout::Action action = getActionFromString(state); 155 if (action != phosphor::led::Layout::Action::Off) 156 { 157 phosphor::led::Layout::LedAction ledAction{ 158 name, action, dutyOn, period, 159 phosphor::led::Layout::Action::On}; 160 physicalLEDStatesPriorToLampTest.emplace(ledAction); 161 } 162 } 163 } 164 165 void LampTest::start() 166 { 167 if (isLampTestRunning) 168 { 169 // reset the timer and then return 170 timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS)); 171 return; 172 } 173 174 // Get paths of all the Physical LED objects 175 physicalLEDPaths = dBusHandler.getSubTreePaths(PHY_LED_PATH, PHY_LED_IFACE); 176 177 // Get physical LEDs states before lamp test 178 storePhysicalLEDsStates(); 179 180 // restart lamp test, it contains initiate or reset the timer. 181 timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS)); 182 isLampTestRunning = true; 183 184 // Notify PHYP to start the lamp test 185 doHostLampTest(true); 186 187 // Set all the Physical action to On for lamp test 188 for (const auto& path : physicalLEDPaths) 189 { 190 auto iter = 191 std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(), 192 [&path](const auto& skip) { return skip == path; }); 193 194 if (iter != skipUpdateLEDs.end()) 195 { 196 // Skip update physical path 197 continue; 198 } 199 200 manager.drivePhysicalLED(path, Layout::Action::On, 0, 0); 201 } 202 } 203 204 void LampTest::timeOutHandler() 205 { 206 // set the Asserted property of lamp test to false 207 if (!groupObj) 208 { 209 lg2::error("the Group object is nullptr"); 210 throw std::runtime_error("the Group object is nullptr"); 211 } 212 213 groupObj->asserted(false); 214 } 215 216 void LampTest::requestHandler(Group* group, bool value) 217 { 218 if (groupObj == NULL) 219 { 220 groupObj = std::move(group); 221 } 222 223 if (value) 224 { 225 start(); 226 } 227 else 228 { 229 stop(); 230 } 231 } 232 233 void LampTest::restorePhysicalLedStates() 234 { 235 // restore physical LEDs states before lamp test 236 ActionSet ledsDeAssert{}; 237 manager.driveLEDs(physicalLEDStatesPriorToLampTest, ledsDeAssert); 238 physicalLEDStatesPriorToLampTest.clear(); 239 240 // restore physical LEDs states during lamp test 241 while (!updatedLEDsDuringLampTest.empty()) 242 { 243 auto& [ledsAssert, ledsDeAssert] = updatedLEDsDuringLampTest.front(); 244 manager.driveLEDs(ledsAssert, ledsDeAssert); 245 updatedLEDsDuringLampTest.pop(); 246 } 247 } 248 249 void LampTest::doHostLampTest(bool value) 250 { 251 try 252 { 253 PropertyValue assertedValue{value}; 254 dBusHandler.setProperty(HOST_LAMP_TEST_OBJECT, 255 "xyz.openbmc_project.Led.Group", "Asserted", 256 assertedValue); 257 } 258 catch (const sdbusplus::exception_t& e) 259 { 260 lg2::error( 261 "Failed to set Asserted property, ERROR = {ERROR}, PATH = {PATH}", 262 "ERROR", e, "PATH", std::string(HOST_LAMP_TEST_OBJECT)); 263 } 264 } 265 266 void LampTest::getPhysicalLEDNamesFromJson(const fs::path& path) 267 { 268 if (!fs::exists(path) || fs::is_empty(path)) 269 { 270 lg2::info("The file does not exist or is empty, FILE_PATH = {PATH}", 271 "PATH", path); 272 return; 273 } 274 275 try 276 { 277 std::ifstream jsonFile(path); 278 auto json = Json::parse(jsonFile); 279 280 // define the default JSON as empty 281 const std::vector<std::string> empty{}; 282 auto forceLEDs = json.value("forceLEDs", empty); 283 std::ranges::transform(forceLEDs, std::back_inserter(forceUpdateLEDs), 284 [](const auto& i) { return PHY_LED_PATH + i; }); 285 286 auto skipLEDs = json.value("skipLEDs", empty); 287 std::ranges::transform(skipLEDs, std::back_inserter(skipUpdateLEDs), 288 [](const auto& i) { return PHY_LED_PATH + i; }); 289 } 290 catch (const std::exception& e) 291 { 292 lg2::error( 293 "Failed to parse config file, ERROR = {ERROR}, FILE_PATH = {PATH}", 294 "ERROR", e, "PATH", path); 295 } 296 return; 297 } 298 299 } // namespace led 300 } // namespace phosphor 301