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