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