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