xref: /openbmc/phosphor-pid-control/pid/zone.hpp (revision 8c051121)
1 #pragma once
2 
3 #include "conf.hpp"
4 #include "controller.hpp"
5 #include "pidcontroller.hpp"
6 #include "sensors/manager.hpp"
7 #include "sensors/sensor.hpp"
8 #include "tuning.hpp"
9 #include "zone_interface.hpp"
10 
11 #include <sdbusplus/bus.hpp>
12 #include <sdbusplus/server.hpp>
13 #include <xyz/openbmc_project/Control/Mode/server.hpp>
14 
15 #include <fstream>
16 #include <iostream>
17 #include <map>
18 #include <memory>
19 #include <set>
20 #include <string>
21 #include <vector>
22 
23 template <typename... T>
24 using ServerObject = typename sdbusplus::server::object_t<T...>;
25 using ModeInterface = sdbusplus::xyz::openbmc_project::Control::server::Mode;
26 using ModeObject = ServerObject<ModeInterface>;
27 
28 namespace pid_control
29 {
30 
31 /*
32  * The DbusPidZone inherits from the Mode object so that it can listen for
33  * control mode changes.  It primarily holds all PID loops and holds the sensor
34  * value cache that's used per iteration of the PID loops.
35  */
36 class DbusPidZone : public ZoneInterface, public ModeObject
37 {
38   public:
39     DbusPidZone(int64_t zone, double minThermalOutput, double failSafePercent,
40                 conf::CycleTime cycleTime, const SensorManager& mgr,
41                 sdbusplus::bus_t& bus, const char* objPath, bool defer) :
42         ModeObject(bus, objPath,
43                    defer ? ModeObject::action::defer_emit
44                          : ModeObject::action::emit_object_added),
45         _zoneId(zone), _maximumSetPoint(),
46         _minThermalOutputSetPt(minThermalOutput),
47         _failSafePercent(failSafePercent), _cycleTime(cycleTime), _mgr(mgr)
48     {
49         if (loggingEnabled)
50         {
51             _log.open(loggingPath + "/zone_" + std::to_string(zone) + ".log");
52         }
53     }
54 
55     bool getManualMode(void) const override;
56     /* Could put lock around this since it's accessed from two threads, but
57      * only one reader/one writer.
58      */
59 
60     bool getRedundantWrite(void) const override;
61     void setManualMode(bool mode);
62     bool getFailSafeMode(void) const override;
63 
64     int64_t getZoneID(void) const override;
65     void addSetPoint(double setPoint, const std::string& name) override;
66     double getMaxSetPointRequest(void) const override;
67     void addRPMCeiling(double ceiling) override;
68     void clearSetPoints(void) override;
69     void clearRPMCeilings(void) override;
70     double getFailSafePercent(void) const override;
71     double getMinThermalSetPoint(void) const;
72     uint64_t getCycleIntervalTime(void) const override;
73     uint64_t getUpdateThermalsCycle(void) const override;
74 
75     Sensor* getSensor(const std::string& name) override;
76     void determineMaxSetPointRequest(void) override;
77     void updateFanTelemetry(void) override;
78     void updateSensors(void) override;
79     void initializeCache(void) override;
80     void setOutputCache(std::string_view, const ValueCacheEntry&) override;
81     void dumpCache(void);
82 
83     void processFans(void) override;
84     void processThermals(void) override;
85 
86     void addFanPID(std::unique_ptr<Controller> pid);
87     void addThermalPID(std::unique_ptr<Controller> pid);
88     double getCachedValue(const std::string& name) override;
89     ValueCacheEntry getCachedValues(const std::string& name) override;
90 
91     void addFanInput(const std::string& fan);
92     void addThermalInput(const std::string& therm);
93 
94     void initializeLog(void) override;
95     void writeLog(const std::string& value) override;
96 
97     /* Method for setting the manual mode over dbus */
98     bool manual(bool value) override;
99     /* Method for reading whether in fail-safe mode over dbus */
100     bool failSafe() const override;
101 
102   private:
103     template <bool fanSensorLogging>
104     void processSensorInputs(const std::vector<std::string>& sensorInputs,
105                              std::chrono::high_resolution_clock::time_point now)
106     {
107         for (const auto& sensorInput : sensorInputs)
108         {
109             auto sensor = _mgr.getSensor(sensorInput);
110             ReadReturn r = sensor->read();
111             _cachedValuesByName[sensorInput] = {r.value, r.unscaled};
112             int64_t timeout = sensor->getTimeout();
113             std::chrono::high_resolution_clock::time_point then = r.updated;
114 
115             auto duration =
116                 std::chrono::duration_cast<std::chrono::seconds>(now - then)
117                     .count();
118             auto period = std::chrono::seconds(timeout).count();
119             /*
120              * TODO(venture): We should check when these were last read.
121              * However, these are the fans, so if I'm not getting updated values
122              * for them... what should I do?
123              */
124             if constexpr (fanSensorLogging)
125             {
126                 if (loggingEnabled)
127                 {
128                     const auto& v = _cachedValuesByName[sensorInput];
129                     _log << "," << v.scaled << "," << v.unscaled;
130                     const auto& p = _cachedFanOutputs[sensorInput];
131                     _log << "," << p.scaled << "," << p.unscaled;
132                 }
133             }
134 
135             if (debugEnabled)
136             {
137                 std::cerr << sensorInput << " sensor reading: " << r.value
138                           << "\n";
139             }
140 
141             // check if fan fail.
142             if (sensor->getFailed())
143             {
144                 _failSafeSensors.insert(sensorInput);
145                 if (debugEnabled)
146                 {
147                     std::cerr << sensorInput << " sensor get failed\n";
148                 }
149             }
150             else if (timeout != 0 && duration >= period)
151             {
152                 _failSafeSensors.insert(sensorInput);
153                 if (debugEnabled)
154                 {
155                     std::cerr << sensorInput << " sensor timeout\n";
156                 }
157             }
158             else
159             {
160                 // Check if it's in there: remove it.
161                 auto kt = _failSafeSensors.find(sensorInput);
162                 if (kt != _failSafeSensors.end())
163                 {
164                     if (debugEnabled)
165                     {
166                         std::cerr << sensorInput
167                                   << " is erased from failsafe sensor set\n";
168                     }
169                     _failSafeSensors.erase(kt);
170                 }
171             }
172         }
173     }
174 
175     std::ofstream _log;
176 
177     const int64_t _zoneId;
178     double _maximumSetPoint = 0;
179     std::string _maximumSetPointName;
180     std::string _maximumSetPointNamePrev;
181     bool _manualMode = false;
182     bool _redundantWrite = false;
183     const double _minThermalOutputSetPt;
184     const double _failSafePercent;
185     const conf::CycleTime _cycleTime;
186 
187     std::set<std::string> _failSafeSensors;
188 
189     std::vector<double> _SetPoints;
190     std::vector<double> _RPMCeilings;
191     std::vector<std::string> _fanInputs;
192     std::vector<std::string> _thermalInputs;
193     std::map<std::string, ValueCacheEntry> _cachedValuesByName;
194     std::map<std::string, ValueCacheEntry> _cachedFanOutputs;
195     const SensorManager& _mgr;
196 
197     std::vector<std::unique_ptr<Controller>> _fans;
198     std::vector<std::unique_ptr<Controller>> _thermals;
199 };
200 
201 } // namespace pid_control
202