xref: /openbmc/phosphor-pid-control/pid/zone.hpp (revision a4270075f7cbdb2dee38f444a59e25b96d8128f4)
1 #pragma once
2 
3 #include "conf.hpp"
4 #include "controller.hpp"
5 #include "failsafeloggers/failsafe_logger_utility.hpp"
6 #include "pidcontroller.hpp"
7 #include "sensors/manager.hpp"
8 #include "sensors/sensor.hpp"
9 #include "tuning.hpp"
10 #include "zone_interface.hpp"
11 
12 #include <sdbusplus/bus.hpp>
13 #include <sdbusplus/server.hpp>
14 #include <xyz/openbmc_project/Control/Mode/server.hpp>
15 #include <xyz/openbmc_project/Debug/Pid/ThermalPower/server.hpp>
16 #include <xyz/openbmc_project/Debug/Pid/Zone/server.hpp>
17 #include <xyz/openbmc_project/Object/Enable/server.hpp>
18 
19 #include <fstream>
20 #include <iostream>
21 #include <map>
22 #include <memory>
23 #include <set>
24 #include <string>
25 #include <vector>
26 
27 template <typename... T>
28 using ServerObject = typename sdbusplus::server::object_t<T...>;
29 using ModeInterface = sdbusplus::xyz::openbmc_project::Control::server::Mode;
30 using DebugZoneInterface =
31     sdbusplus::xyz::openbmc_project::Debug::Pid::server::Zone;
32 using ModeObject = ServerObject<ModeInterface, DebugZoneInterface>;
33 using ProcessInterface =
34     sdbusplus::xyz::openbmc_project::Object::server::Enable;
35 using DebugThermalPowerInterface =
36     sdbusplus::xyz::openbmc_project::Debug::Pid::server::ThermalPower;
37 using ProcessObject =
38     ServerObject<ProcessInterface, DebugThermalPowerInterface>;
39 using FailSafeSensorsMap =
40     std::map<std::string, std::pair<std::string, double>>;
41 using FailSafeSensorPair =
42     std::pair<std::string, std::pair<std::string, double>>;
43 
44 namespace pid_control
45 {
46 
47 /*
48  * The DbusPidZone inherits from the Mode object so that it can listen for
49  * control mode changes.  It primarily holds all PID loops and holds the sensor
50  * value cache that's used per iteration of the PID loops.
51  */
52 class DbusPidZone : public ZoneInterface, public ModeObject
53 {
54   public:
DbusPidZone(int64_t zone,double minThermalOutput,double failSafePercent,conf::CycleTime cycleTime,const SensorManager & mgr,sdbusplus::bus_t & bus,const char * objPath,bool defer,bool accumulateSetPoint)55     DbusPidZone(int64_t zone, double minThermalOutput, double failSafePercent,
56                 conf::CycleTime cycleTime, const SensorManager& mgr,
57                 sdbusplus::bus_t& bus, const char* objPath, bool defer,
58                 bool accumulateSetPoint) :
59         ModeObject(bus, objPath,
60                    defer ? ModeObject::action::defer_emit
61                          : ModeObject::action::emit_object_added),
62         _zoneId(zone), _maximumSetPoint(),
63         _accumulateSetPoint(accumulateSetPoint),
64         _minThermalOutputSetPt(minThermalOutput),
65         _zoneFailSafePercent(failSafePercent), _cycleTime(cycleTime), _mgr(mgr)
66     {
67         if (loggingEnabled)
68         {
69             _log.open(loggingPath + "/zone_" + std::to_string(zone) + ".log");
70         }
71     }
72 
73     bool getManualMode(void) const override;
74     /* Could put lock around this since it's accessed from two threads, but
75      * only one reader/one writer.
76      */
77 
78     bool getRedundantWrite(void) const override;
79     void setManualMode(bool mode);
80     bool getFailSafeMode(void) const override;
81     void markSensorMissing(const std::string& name,
82                            const std::string& failReason);
83     bool getAccSetPoint(void) const override;
84 
85     int64_t getZoneID(void) const override;
86     void addSetPoint(double setPoint, const std::string& name) override;
87     double getMaxSetPointRequest(void) const override;
88     void addRPMCeiling(double ceiling) override;
89     void clearSetPoints(void) override;
90     void clearRPMCeilings(void) override;
91     double getFailSafePercent(void) override;
92     FailSafeSensorsMap getFailSafeSensors(void) const override;
93     double getMinThermalSetPoint(void) const;
94     uint64_t getCycleIntervalTime(void) const override;
95     uint64_t getUpdateThermalsCycle(void) const override;
96 
97     Sensor* getSensor(const std::string& name) override;
98     std::vector<std::string> getSensorNames(void) override;
99     void determineMaxSetPointRequest(void) override;
100     void updateFanTelemetry(void) override;
101     void updateSensors(void) override;
102     void initializeCache(void) override;
103     void setOutputCache(std::string_view, const ValueCacheEntry&) override;
104     void dumpCache(void);
105 
106     void processFans(void) override;
107     void processThermals(void) override;
108 
109     void addFanPID(std::unique_ptr<Controller> pid);
110     void addThermalPID(std::unique_ptr<Controller> pid);
111     double getCachedValue(const std::string& name) override;
112     ValueCacheEntry getCachedValues(const std::string& name) override;
113 
114     void addFanInput(const std::string& fan, bool missingAcceptable);
115     void addThermalInput(const std::string& therm, bool missingAcceptable);
116 
117     void initializeLog(void) override;
118     void writeLog(const std::string& value) override;
119 
120     /* Method for setting the manual mode over dbus */
121     bool manual(bool value) override;
122     /* Method for reading whether in fail-safe mode over dbus */
123     bool failSafe() const override;
124     /* Method for recording the maximum SetPoint PID config name */
125     std::string leader() const override;
126     /* Method for control process for each loop at runtime */
127     void addPidControlProcess(std::string name, std::string type,
128                               double setpoint, sdbusplus::bus_t& bus,
129                               std::string objPath, bool defer);
130     bool isPidProcessEnabled(std::string name);
131 
132     void addPidFailSafePercent(std::vector<std::string> inputs, double percent);
133 
134     void updateThermalPowerDebugInterface(std::string pidName,
135                                           std::string leader, double input,
136                                           double output) override;
137 
138   private:
139     template <bool fanSensorLogging>
processSensorInputs(const std::vector<std::string> & sensorInputs,std::chrono::high_resolution_clock::time_point now)140     void processSensorInputs(const std::vector<std::string>& sensorInputs,
141                              std::chrono::high_resolution_clock::time_point now)
142     {
143         for (const auto& sensorInput : sensorInputs)
144         {
145             auto sensor = _mgr.getSensor(sensorInput);
146             ReadReturn r = sensor->read();
147             _cachedValuesByName[sensorInput] = {r.value, r.unscaled};
148             int64_t timeout = sensor->getTimeout();
149             std::chrono::high_resolution_clock::time_point then = r.updated;
150 
151             auto duration =
152                 std::chrono::duration_cast<std::chrono::seconds>(now - then)
153                     .count();
154             auto period = std::chrono::seconds(timeout).count();
155             /*
156              * TODO(venture): We should check when these were last read.
157              * However, these are the fans, so if I'm not getting updated values
158              * for them... what should I do?
159              */
160             if constexpr (fanSensorLogging)
161             {
162                 if (loggingEnabled)
163                 {
164                     const auto& v = _cachedValuesByName[sensorInput];
165                     _log << "," << v.scaled << "," << v.unscaled;
166                     const auto& p = _cachedFanOutputs[sensorInput];
167                     _log << "," << p.scaled << "," << p.unscaled;
168                 }
169             }
170 
171             if (debugEnabled)
172             {
173                 std::cerr << sensorInput << " sensor reading: " << r.value
174                           << "\n";
175             }
176 
177             // check if fan fail.
178             if (sensor->getFailed())
179             {
180                 markSensorMissing(sensorInput, sensor->getFailReason());
181 
182                 if (debugEnabled)
183                 {
184                     std::cerr << sensorInput << " sensor get failed\n";
185                 }
186             }
187             else if (timeout != 0 && duration >= period)
188             {
189                 markSensorMissing(sensorInput, "Sensor timeout");
190 
191                 if (debugEnabled)
192                 {
193                     std::cerr << sensorInput << " sensor timeout\n";
194                 }
195                 outputFailsafeLogWithZone(_zoneId, this->getFailSafeMode(),
196                                           sensorInput,
197                                           "The sensor has timed out.");
198             }
199             else
200             {
201                 // Check if it's in there: remove it.
202                 auto kt = _failSafeSensors.find(sensorInput);
203                 if (kt != _failSafeSensors.end())
204                 {
205                     if (debugEnabled)
206                     {
207                         std::cerr << sensorInput
208                                   << " is erased from failsafe sensor set\n";
209                     }
210 
211                     _failSafeSensors.erase(kt);
212                     outputFailsafeLogWithZone(_zoneId, this->getFailSafeMode(),
213                                               sensorInput,
214                                               "The sensor has recovered.");
215                 }
216             }
217         }
218     }
219 
220     std::ofstream _log;
221 
222     const int64_t _zoneId;
223     double _maximumSetPoint = 0;
224     std::string _maximumSetPointName;
225     std::string _maximumSetPointNamePrev;
226     bool _manualMode = false;
227     bool _redundantWrite = false;
228     bool _accumulateSetPoint = false;
229     const double _minThermalOutputSetPt;
230     // Zone fail safe Percent setting by configuration.
231     const double _zoneFailSafePercent;
232     const conf::CycleTime _cycleTime;
233 
234     /*
235      * <map key = sensor name, value = sensor fail reason and failsafe percent>
236      */
237     FailSafeSensorsMap _failSafeSensors;
238     std::set<std::string> _missingAcceptable;
239 
240     std::map<std::string, double> _SetPoints;
241     std::vector<double> _RPMCeilings;
242     std::vector<std::string> _fanInputs;
243     std::vector<std::string> _thermalInputs;
244     std::map<std::string, ValueCacheEntry> _cachedValuesByName;
245     std::map<std::string, ValueCacheEntry> _cachedFanOutputs;
246     const SensorManager& _mgr;
247 
248     std::vector<std::unique_ptr<Controller>> _fans;
249     std::vector<std::unique_ptr<Controller>> _thermals;
250 
251     std::map<std::string, std::unique_ptr<ProcessObject>> _pidsControlProcess;
252     /*
253      * <key = sensor name, value = sensor failsafe percent>
254      * sensor fail safe Percent setting by each pid controller configuration.
255      */
256     std::map<std::string, double> _sensorFailSafePercent;
257 };
258 
259 } // namespace pid_control
260