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      */
73     explicit ActionEnvironment(const IDMap& idMap, const std::string& deviceID,
74                                Services& services) :
75         idMap{idMap},
76         deviceID{deviceID}, services{services}
77     {}
78 
79     /**
80      * Adds the specified key/value pair to the map of additional error data
81      * that has been captured.
82      *
83      * This data provides more information about an error and will be stored in
84      * the error log.
85      *
86      * @param key key name
87      * @param value value expressed as a string
88      */
89     void addAdditionalErrorData(const std::string& key,
90                                 const std::string& value)
91     {
92         additionalErrorData.emplace(key, value);
93     }
94 
95     /**
96      * Adds the specified phase fault to the set of faults that have been
97      * detected.
98      *
99      * @param type phase fault type
100      */
101     void addPhaseFault(PhaseFaultType type)
102     {
103         phaseFaults.emplace(type);
104     }
105 
106     /**
107      * Decrements the rule call stack depth by one.
108      *
109      * Should be used when a call to a rule returns.  Does nothing if depth is
110      * already 0.
111      */
112     void decrementRuleDepth()
113     {
114         if (ruleDepth > 0)
115         {
116             --ruleDepth;
117         }
118     }
119 
120     /**
121      * Returns the additional error data that has been captured (if any).
122      *
123      * @return additional error data
124      */
125     const std::map<std::string, std::string>& getAdditionalErrorData() const
126     {
127         return additionalErrorData;
128     }
129 
130     /**
131      * Returns the device with the current device ID.
132      *
133      * Throws invalid_argument if no device is found with current ID.
134      *
135      * @return device with current device ID
136      */
137     Device& getDevice() const
138     {
139         return idMap.getDevice(deviceID);
140     }
141 
142     /**
143      * Returns the current device ID.
144      *
145      * @return current device ID
146      */
147     const std::string& getDeviceID() const
148     {
149         return deviceID;
150     }
151 
152     /**
153      * Returns the set of phase faults that have been detected (if any).
154      *
155      * @return phase faults detected
156      */
157     const std::set<PhaseFaultType>& getPhaseFaults() const
158     {
159         return phaseFaults;
160     }
161 
162     /**
163      * Returns the rule with the specified ID.
164      *
165      * Throws invalid_argument if no rule is found with specified ID.
166      *
167      * @param id rule ID
168      * @return rule with specified ID
169      */
170     Rule& getRule(const std::string& id) const
171     {
172         return idMap.getRule(id);
173     }
174 
175     /**
176      * Returns the current rule call stack depth.
177      *
178      * The depth is 0 if no rules have been called.
179      *
180      * @return rule call stack depth
181      */
182     size_t getRuleDepth() const
183     {
184         return ruleDepth;
185     }
186 
187     /**
188      * Returns the services in this action environment.
189      *
190      * @return system services
191      */
192     Services& getServices() const
193     {
194         return services;
195     }
196 
197     /**
198      * Returns the current volts value, if set.
199      *
200      * @return current volts value
201      */
202     std::optional<double> getVolts() const
203     {
204         return volts;
205     }
206 
207     /**
208      * Increments the rule call stack depth by one.
209      *
210      * Should be used when a rule is called.
211      *
212      * Throws runtime_error if the new depth exceeds maxRuleDepth.  This
213      * indicates that infinite recursion has probably occurred (rule A -> rule B
214      * -> rule A).
215      *
216      * @param ruleID ID of the rule that is being called
217      */
218     void incrementRuleDepth(const std::string& ruleID)
219     {
220         if (ruleDepth >= maxRuleDepth)
221         {
222             throw std::runtime_error("Maximum rule depth exceeded by rule " +
223                                      ruleID + '.');
224         }
225         ++ruleDepth;
226     }
227 
228     /**
229      * Sets the current device ID.
230      *
231      * @param id device ID
232      */
233     void setDeviceID(const std::string& id)
234     {
235         deviceID = id;
236     }
237 
238     /**
239      * Sets the current volts value.
240      *
241      * @param volts new volts value.
242      */
243     void setVolts(double volts)
244     {
245         this->volts = volts;
246     }
247 
248   private:
249     /**
250      * Mapping from string IDs to the associated Device and Rule objects.
251      */
252     const IDMap& idMap;
253 
254     /**
255      * Current device ID.
256      */
257     std::string deviceID{};
258 
259     /**
260      * System services like error logging and the journal.
261      */
262     Services& services;
263 
264     /**
265      * Current volts value (if set).
266      */
267     std::optional<double> volts{};
268 
269     /**
270      * Rule call stack depth.
271      */
272     size_t ruleDepth{0};
273 
274     /**
275      * Redundant phase faults that have been detected.
276      */
277     std::set<PhaseFaultType> phaseFaults{};
278 
279     /**
280      * Additional error data that has been captured.
281      */
282     std::map<std::string, std::string> additionalErrorData{};
283 };
284 
285 } // namespace phosphor::power::regulators
286