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