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 #include "phase_fault.hpp"
20 #include "services.hpp"
21 
22 #include <cstddef> // for size_t
23 #include <map>
24 #include <optional>
25 #include <set>
26 #include <stdexcept>
27 #include <string>
28 
29 namespace phosphor::power::regulators
30 {
31 
32 // Forward declarations to avoid circular dependencies
33 class Device;
34 class Rule;
35 
36 /**
37  * @class ActionEnvironment
38  *
39  * The current environment when executing actions.
40  *
41  * The ActionEnvironment contains the following information:
42  *   - current device ID
43  *   - current volts value (if any)
44  *   - mapping from device and rule IDs to the corresponding objects
45  *   - rule call stack depth (to detect infinite recursion)
46  *   - reference to system services
47  *   - faults detected by actions (if any)
48  *   - additional error data captured by actions (if any)
49  */
50 class ActionEnvironment
51 {
52   public:
53     // Specify which compiler-generated methods we want
54     ActionEnvironment() = delete;
55     ActionEnvironment(const ActionEnvironment&) = delete;
56     ActionEnvironment(ActionEnvironment&&) = delete;
57     ActionEnvironment& operator=(const ActionEnvironment&) = delete;
58     ActionEnvironment& operator=(ActionEnvironment&&) = delete;
59     ~ActionEnvironment() = default;
60 
61     /**
62      * Maximum rule call stack depth.  Used to detect infinite recursion.
63      */
64     static constexpr size_t maxRuleDepth{30};
65 
66     /**
67      * Constructor.
68      *
69      * @param idMap mapping from IDs to the associated Device/Rule objects
70      * @param deviceID current device ID
71      * @param services system services like error logging and the journal
72      */
ActionEnvironment(const IDMap & idMap,const std::string & deviceID,Services & services)73     explicit ActionEnvironment(const IDMap& idMap, const std::string& deviceID,
74                                Services& services) :
75         idMap{idMap}, deviceID{deviceID}, services{services}
76     {}
77 
78     /**
79      * Adds the specified key/value pair to the map of additional error data
80      * that has been captured.
81      *
82      * This data provides more information about an error and will be stored in
83      * the error log.
84      *
85      * @param key key name
86      * @param value value expressed as a string
87      */
addAdditionalErrorData(const std::string & key,const std::string & value)88     void addAdditionalErrorData(const std::string& key,
89                                 const std::string& value)
90     {
91         additionalErrorData.emplace(key, value);
92     }
93 
94     /**
95      * Adds the specified phase fault to the set of faults that have been
96      * detected.
97      *
98      * @param type phase fault type
99      */
addPhaseFault(PhaseFaultType type)100     void addPhaseFault(PhaseFaultType type)
101     {
102         phaseFaults.emplace(type);
103     }
104 
105     /**
106      * Decrements the rule call stack depth by one.
107      *
108      * Should be used when a call to a rule returns.  Does nothing if depth is
109      * already 0.
110      */
decrementRuleDepth()111     void decrementRuleDepth()
112     {
113         if (ruleDepth > 0)
114         {
115             --ruleDepth;
116         }
117     }
118 
119     /**
120      * Returns the additional error data that has been captured (if any).
121      *
122      * @return additional error data
123      */
getAdditionalErrorData() const124     const std::map<std::string, std::string>& getAdditionalErrorData() const
125     {
126         return additionalErrorData;
127     }
128 
129     /**
130      * Returns the device with the current device ID.
131      *
132      * Throws invalid_argument if no device is found with current ID.
133      *
134      * @return device with current device ID
135      */
getDevice() const136     Device& getDevice() const
137     {
138         return idMap.getDevice(deviceID);
139     }
140 
141     /**
142      * Returns the current device ID.
143      *
144      * @return current device ID
145      */
getDeviceID() const146     const std::string& getDeviceID() const
147     {
148         return deviceID;
149     }
150 
151     /**
152      * Returns the set of phase faults that have been detected (if any).
153      *
154      * @return phase faults detected
155      */
getPhaseFaults() const156     const std::set<PhaseFaultType>& getPhaseFaults() const
157     {
158         return phaseFaults;
159     }
160 
161     /**
162      * Returns the rule with the specified ID.
163      *
164      * Throws invalid_argument if no rule is found with specified ID.
165      *
166      * @param id rule ID
167      * @return rule with specified ID
168      */
getRule(const std::string & id) const169     Rule& getRule(const std::string& id) const
170     {
171         return idMap.getRule(id);
172     }
173 
174     /**
175      * Returns the current rule call stack depth.
176      *
177      * The depth is 0 if no rules have been called.
178      *
179      * @return rule call stack depth
180      */
getRuleDepth() const181     size_t getRuleDepth() const
182     {
183         return ruleDepth;
184     }
185 
186     /**
187      * Returns the services in this action environment.
188      *
189      * @return system services
190      */
getServices() const191     Services& getServices() const
192     {
193         return services;
194     }
195 
196     /**
197      * Returns the current volts value, if set.
198      *
199      * @return current volts value
200      */
getVolts() const201     std::optional<double> getVolts() const
202     {
203         return volts;
204     }
205 
206     /**
207      * Increments the rule call stack depth by one.
208      *
209      * Should be used when a rule is called.
210      *
211      * Throws runtime_error if the new depth exceeds maxRuleDepth.  This
212      * indicates that infinite recursion has probably occurred (rule A -> rule B
213      * -> rule A).
214      *
215      * @param ruleID ID of the rule that is being called
216      */
incrementRuleDepth(const std::string & ruleID)217     void incrementRuleDepth(const std::string& ruleID)
218     {
219         if (ruleDepth >= maxRuleDepth)
220         {
221             throw std::runtime_error(
222                 "Maximum rule depth exceeded by rule " + ruleID + '.');
223         }
224         ++ruleDepth;
225     }
226 
227     /**
228      * Sets the current device ID.
229      *
230      * @param id device ID
231      */
setDeviceID(const std::string & id)232     void setDeviceID(const std::string& id)
233     {
234         deviceID = id;
235     }
236 
237     /**
238      * Sets the current volts value.
239      *
240      * @param volts new volts value.
241      */
setVolts(double volts)242     void setVolts(double volts)
243     {
244         this->volts = volts;
245     }
246 
247   private:
248     /**
249      * Mapping from string IDs to the associated Device and Rule objects.
250      */
251     const IDMap& idMap;
252 
253     /**
254      * Current device ID.
255      */
256     std::string deviceID{};
257 
258     /**
259      * System services like error logging and the journal.
260      */
261     Services& services;
262 
263     /**
264      * Current volts value (if set).
265      */
266     std::optional<double> volts{};
267 
268     /**
269      * Rule call stack depth.
270      */
271     size_t ruleDepth{0};
272 
273     /**
274      * Redundant phase faults that have been detected.
275      */
276     std::set<PhaseFaultType> phaseFaults{};
277 
278     /**
279      * Additional error data that has been captured.
280      */
281     std::map<std::string, std::string> additionalErrorData{};
282 };
283 
284 } // namespace phosphor::power::regulators
285