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