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