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