1 /** 2 * Copyright © 2019 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 "id_map.hpp" 19 20 #include <cstddef> // for size_t 21 #include <stdexcept> 22 #include <string> 23 24 namespace phosphor::power::regulators 25 { 26 27 // Forward declarations to avoid circular dependencies 28 class Device; 29 class Rule; 30 31 /** 32 * @class ActionEnvironment 33 * 34 * The current environment when executing actions. 35 * 36 * The ActionEnvironment contains the following information: 37 * - current device ID 38 * - current volts value (if any) 39 * - mapping from device and rule IDs to the corresponding objects 40 * - rule call stack depth (to detect infinite recursion) 41 */ 42 class ActionEnvironment 43 { 44 public: 45 // Specify which compiler-generated methods we want 46 ActionEnvironment() = delete; 47 ActionEnvironment(const ActionEnvironment&) = delete; 48 ActionEnvironment(ActionEnvironment&&) = delete; 49 ActionEnvironment& operator=(const ActionEnvironment&) = delete; 50 ActionEnvironment& operator=(ActionEnvironment&&) = delete; 51 ~ActionEnvironment() = default; 52 53 /** 54 * Maximum rule call stack depth. Used to detect infinite recursion. 55 */ 56 static constexpr size_t maxRuleDepth{30}; 57 58 /** 59 * Constructor. 60 * 61 * @param idMap mapping from IDs to the associated Device/Rule objects 62 * @param deviceID current device ID 63 */ 64 explicit ActionEnvironment(const IDMap& idMap, 65 const std::string& deviceID) : 66 idMap{idMap}, 67 deviceID{deviceID} 68 { 69 } 70 71 /** 72 * Decrements the rule call stack depth by one. 73 * 74 * Should be used when a call to a rule returns. Does nothing if depth is 75 * already 0. 76 */ 77 void decrementRuleDepth() 78 { 79 if (ruleDepth > 0) 80 { 81 --ruleDepth; 82 } 83 } 84 85 /** 86 * Returns the device with the current device ID. 87 * 88 * Throws invalid_argument if no device is found with current ID. 89 * 90 * @return device with current device ID 91 */ 92 Device& getDevice() const 93 { 94 return idMap.getDevice(deviceID); 95 } 96 97 /** 98 * Returns the current device ID. 99 * 100 * @return current device ID 101 */ 102 const std::string& getDeviceID() const 103 { 104 return deviceID; 105 } 106 107 /** 108 * Returns the rule with the specified ID. 109 * 110 * Throws invalid_argument if no rule is found with specified ID. 111 * 112 * @param id rule ID 113 * @return rule with specified ID 114 */ 115 Rule& getRule(const std::string& id) const 116 { 117 return idMap.getRule(id); 118 } 119 120 /** 121 * Returns the current rule call stack depth. 122 * 123 * The depth is 0 if no rules have been called. 124 * 125 * @return rule call stack depth 126 */ 127 size_t getRuleDepth() const 128 { 129 return ruleDepth; 130 } 131 132 /** 133 * Returns the current volts value. 134 * 135 * Call hasVolts() first to check whether a volts value has been set. 136 * 137 * Throws logic_error if no volts value has been set. 138 * 139 * @return current volts value 140 */ 141 double getVolts() const 142 { 143 if (!hasVoltsValue) 144 { 145 throw std::logic_error{"No volts value has been set."}; 146 } 147 return volts; 148 } 149 150 /** 151 * Returns whether a volts value has been set. 152 * 153 * @return true if a volts value has been set, false otherwise 154 */ 155 bool hasVolts() const 156 { 157 return hasVoltsValue; 158 } 159 160 /** 161 * Increments the rule call stack depth by one. 162 * 163 * Should be used when a rule is called. 164 * 165 * Throws runtime_error if the new depth exceeds maxRuleDepth. This 166 * indicates that infinite recursion has probably occurred (rule A -> rule B 167 * -> rule A). 168 * 169 * @param ruleID ID of the rule that is being called 170 */ 171 void incrementRuleDepth(const std::string& ruleID) 172 { 173 if (ruleDepth >= maxRuleDepth) 174 { 175 throw std::runtime_error("Maximum rule depth exceeded by rule " + 176 ruleID + '.'); 177 } 178 ++ruleDepth; 179 } 180 181 /** 182 * Sets the current device ID. 183 * 184 * @param id device ID 185 */ 186 void setDeviceID(const std::string& id) 187 { 188 deviceID = id; 189 } 190 191 /** 192 * Sets the current volts value. 193 * 194 * @param volts new volts value. 195 */ 196 void setVolts(double volts) 197 { 198 this->volts = volts; 199 hasVoltsValue = true; 200 } 201 202 private: 203 /** 204 * Mapping from string IDs to the associated Device and Rule objects. 205 */ 206 const IDMap& idMap; 207 208 /** 209 * Current device ID. 210 */ 211 std::string deviceID{}; 212 213 /** 214 * Indicates whether a volts value has been set. 215 */ 216 bool hasVoltsValue{false}; 217 218 /** 219 * Current volts value (if set). 220 */ 221 double volts{0}; 222 223 /** 224 * Rule call stack depth. 225 */ 226 size_t ruleDepth{0}; 227 }; 228 229 } // namespace phosphor::power::regulators 230