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