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