1 /** 2 * Copyright © 2025 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #pragma once 17 18 #include "chassis.hpp" 19 #include "power_interface.hpp" 20 #include "services.hpp" 21 22 #include <stddef.h> // for size_t 23 24 #include <memory> 25 #include <optional> 26 #include <set> 27 #include <stdexcept> 28 #include <utility> 29 #include <vector> 30 31 namespace phosphor::power::sequencer 32 { 33 34 /** 35 * @class System 36 * 37 * The computer system being controlled and monitored by the BMC. 38 * 39 * The system contains one or more chassis. 40 */ 41 class System 42 { 43 public: 44 System() = delete; 45 System(const System&) = delete; 46 System(System&&) = delete; 47 System& operator=(const System&) = delete; 48 System& operator=(System&&) = delete; 49 ~System() = default; 50 51 /** 52 * Constructor. 53 * 54 * @param chassis Chassis in the system 55 */ System(std::vector<std::unique_ptr<Chassis>> chassis)56 explicit System(std::vector<std::unique_ptr<Chassis>> chassis) : 57 chassis{std::move(chassis)} 58 {} 59 60 /** 61 * Returns the chassis in the system. 62 * 63 * @return chassis 64 */ getChassis() const65 const std::vector<std::unique_ptr<Chassis>>& getChassis() const 66 { 67 return chassis; 68 } 69 70 /** 71 * Initializes system monitoring. 72 * 73 * This method must be called before any methods that return or check the 74 * system status. 75 * 76 * Normally this method is only called once. However, it can be called 77 * multiple times if required, such as for automated testing. 78 * 79 * @param services System services like hardware presence and the journal 80 */ 81 void initializeMonitoring(Services& services); 82 83 /** 84 * Returns the last requested system power state. 85 * 86 * The initial power state is obtained by the monitor() method. That method 87 * must be called before calling getPowerState(). 88 * 89 * Throws an exception if the power state could not be obtained. 90 * 91 * @return last requested power state 92 */ getPowerState()93 PowerState getPowerState() 94 { 95 if (!powerState) 96 { 97 throw std::runtime_error{ 98 "System power state could not be obtained"}; 99 } 100 return *powerState; 101 } 102 103 /** 104 * Sets the requested system power state. 105 * 106 * Powers the system on or off based on the specified state. 107 * 108 * Throws an exception if one of the following occurs: 109 * - System monitoring has not been initialized 110 * - System cannot be set to the specified power state 111 * - No chassis can be set to the specified power state 112 * 113 * @param newPowerState New system power state 114 * @param services System services like hardware presence and the journal 115 */ 116 void setPowerState(PowerState newPowerState, Services& services); 117 118 /** 119 * Returns the chassis numbers selected for the current power on/off 120 * attempt. 121 * 122 * @return chassis numbers in current power on/off attempt 123 */ getSelectedChassis()124 const std::set<size_t>& getSelectedChassis() 125 { 126 return selectedChassis; 127 } 128 129 /** 130 * Returns the system power good value. 131 * 132 * The power good value is set by the monitor() method. That method must be 133 * called before calling getPowerGood(). 134 * 135 * Throws an exception if the power good value could not be obtained. 136 * 137 * @return system power good 138 */ getPowerGood()139 PowerGood getPowerGood() 140 { 141 if (!powerGood) 142 { 143 throw std::runtime_error{"System power good could not be obtained"}; 144 } 145 return *powerGood; 146 } 147 148 /** 149 * Monitors the status of the system. 150 * 151 * Sets the system power good value by obtaining the power good value from 152 * each chassis selected for the current power on/off attempt. 153 * 154 * This method must be called periodically (such as once per second) to 155 * ensure the system power state and power good values are correct. 156 * 157 * Throws an exception if system monitoring has not been initialized. 158 * 159 * @param services System services like hardware presence and the journal 160 */ 161 void monitor(Services& services); 162 163 private: 164 /** 165 * Verifies that system monitoring has been initialized. 166 * 167 * Throws an exception if monitoring has not been initialized. 168 */ verifyMonitoringInitialized()169 void verifyMonitoringInitialized() 170 { 171 if (!isMonitoringInitialized) 172 { 173 throw std::runtime_error{ 174 "System monitoring has not been initialized"}; 175 } 176 } 177 178 /** 179 * Verifies that the system can be set to the specified power state. 180 * 181 * Throws an exception if the new power state is not allowed. 182 * 183 * @param newPowerState New system power state 184 */ verifyCanSetPowerState(PowerState newPowerState)185 void verifyCanSetPowerState(PowerState newPowerState) 186 { 187 if (powerState && (powerState == newPowerState)) 188 { 189 throw std::runtime_error{std::format( 190 "Unable to set system to state {}: Already at requested state", 191 PowerInterface::toString(newPowerState))}; 192 } 193 } 194 195 /** 196 * Returns the set of chassis numbers that can be set to the specified new 197 * power state. 198 * 199 * Throws an exception if system monitoring has not been initialized. 200 * 201 * @param newPowerState New system power state 202 * @param services System services like hardware presence and the journal 203 * @return set of chassis numbers that can be set to new power state 204 */ 205 std::set<size_t> getChassisForNewPowerState(PowerState newPowerState, 206 Services& services); 207 208 /** 209 * Defines the initial set of chassis selected for power on/off if needed. 210 * 211 * This is necessary when the application first starts. 212 * 213 * We do not know which chassis were selected for power on/off prior to the 214 * application starting. 215 * 216 * Define the selected chassis set based on which chassis are currently 217 * powered on and off. 218 * 219 * The selected chassis will be set explicitly next time the system is 220 * powered on or off by setPowerState(). 221 */ 222 void setInitialSelectedChassisIfNeeded(); 223 224 /** 225 * Sets the system power good value based on the chassis power good values. 226 */ 227 void setPowerGood(); 228 229 /** 230 * Sets the initial power state value if it currently has no value. 231 * 232 * This is necessary when the application first starts. 233 * 234 * The initial power state value is based on the current power good value. 235 * We assume that the last requested power state matches the power good 236 * value. For example, if the system power good is on, then we assume the 237 * last requested system power state was on. 238 * 239 * The power state value will be set explicitly next time the system is 240 * powered on or off by setPowerState(). 241 */ 242 void setInitialPowerStateIfNeeded(); 243 244 /** 245 * Chassis in the system. 246 */ 247 std::vector<std::unique_ptr<Chassis>> chassis{}; 248 249 /** 250 * Indicates whether system monitoring has been initialized. 251 */ 252 bool isMonitoringInitialized{false}; 253 254 /** 255 * Last requested system power state. 256 */ 257 std::optional<PowerState> powerState{}; 258 259 /** 260 * System power good. 261 */ 262 std::optional<PowerGood> powerGood{}; 263 264 /** 265 * Chassis numbers that were selected for the current power on/off attempt. 266 */ 267 std::set<size_t> selectedChassis; 268 }; 269 270 } // namespace phosphor::power::sequencer 271