xref: /openbmc/phosphor-pid-control/pid/zone.cpp (revision 6df8bb5086b29c43217596b194dda7fbc4e3ec4a)
1d8012181SPatrick Venture /**
2d8012181SPatrick Venture  * Copyright 2017 Google Inc.
3d8012181SPatrick Venture  *
4d8012181SPatrick Venture  * Licensed under the Apache License, Version 2.0 (the "License");
5d8012181SPatrick Venture  * you may not use this file except in compliance with the License.
6d8012181SPatrick Venture  * You may obtain a copy of the License at
7d8012181SPatrick Venture  *
8d8012181SPatrick Venture  *     http://www.apache.org/licenses/LICENSE-2.0
9d8012181SPatrick Venture  *
10d8012181SPatrick Venture  * Unless required by applicable law or agreed to in writing, software
11d8012181SPatrick Venture  * distributed under the License is distributed on an "AS IS" BASIS,
12d8012181SPatrick Venture  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d8012181SPatrick Venture  * See the License for the specific language governing permissions and
14d8012181SPatrick Venture  * limitations under the License.
15d8012181SPatrick Venture  */
16d8012181SPatrick Venture 
17d8012181SPatrick Venture /* Configuration. */
18d8012181SPatrick Venture #include "zone.hpp"
19d8012181SPatrick Venture 
20da4a5dd1SPatrick Venture #include "conf.hpp"
21*6df8bb50SJames Zheng #include "failsafeloggers/failsafe_logger_utility.hpp"
22da4a5dd1SPatrick Venture #include "pid/controller.hpp"
23da4a5dd1SPatrick Venture #include "pid/ec/pid.hpp"
24da4a5dd1SPatrick Venture #include "pid/fancontroller.hpp"
2522c257abSJames Feist #include "pid/stepwisecontroller.hpp"
26da4a5dd1SPatrick Venture #include "pid/thermalcontroller.hpp"
27c32e3fc5SPatrick Venture #include "pid/tuning.hpp"
28da4a5dd1SPatrick Venture 
29d8012181SPatrick Venture #include <algorithm>
30d8012181SPatrick Venture #include <chrono>
31d8012181SPatrick Venture #include <cstring>
32d8012181SPatrick Venture #include <fstream>
33d8012181SPatrick Venture #include <iostream>
34d8012181SPatrick Venture #include <memory>
3555ccad65SJosh Lehan #include <sstream>
367a98c19aSPatrick Venture #include <string>
37d8012181SPatrick Venture 
38d8012181SPatrick Venture using tstamp = std::chrono::high_resolution_clock::time_point;
39d8012181SPatrick Venture using namespace std::literals::chrono_literals;
40d8012181SPatrick Venture 
4155ccad65SJosh Lehan // Enforces minimum duration between events
4255ccad65SJosh Lehan // Rreturns true if event should be allowed, false if disallowed
allowThrottle(const tstamp & now,const std::chrono::seconds & pace)4355ccad65SJosh Lehan bool allowThrottle(const tstamp& now, const std::chrono::seconds& pace)
4455ccad65SJosh Lehan {
4555ccad65SJosh Lehan     static tstamp then;
4655ccad65SJosh Lehan     static bool first = true;
4755ccad65SJosh Lehan 
4855ccad65SJosh Lehan     if (first)
4955ccad65SJosh Lehan     {
5055ccad65SJosh Lehan         // Special case initialization
5155ccad65SJosh Lehan         then = now;
5255ccad65SJosh Lehan         first = false;
5355ccad65SJosh Lehan 
5455ccad65SJosh Lehan         // Initialization, always allow
5555ccad65SJosh Lehan         return true;
5655ccad65SJosh Lehan     }
5755ccad65SJosh Lehan 
5855ccad65SJosh Lehan     auto elapsed = now - then;
5955ccad65SJosh Lehan     if (elapsed < pace)
6055ccad65SJosh Lehan     {
6155ccad65SJosh Lehan         // Too soon since last time, disallow
6255ccad65SJosh Lehan         return false;
6355ccad65SJosh Lehan     }
6455ccad65SJosh Lehan 
6555ccad65SJosh Lehan     // It has been long enough, allow
6655ccad65SJosh Lehan     then = now;
6755ccad65SJosh Lehan     return true;
6855ccad65SJosh Lehan }
6955ccad65SJosh Lehan 
7055ccad65SJosh Lehan namespace pid_control
7155ccad65SJosh Lehan {
7255ccad65SJosh Lehan 
getMaxSetPointRequest(void) const73597ebd67SPatrick Venture double DbusPidZone::getMaxSetPointRequest(void) const
74d8012181SPatrick Venture {
75f7a2dd5cSPatrick Venture     return _maximumSetPoint;
76d8012181SPatrick Venture }
77d8012181SPatrick Venture 
getManualMode(void) const78597ebd67SPatrick Venture bool DbusPidZone::getManualMode(void) const
79d8012181SPatrick Venture {
80d8012181SPatrick Venture     return _manualMode;
81d8012181SPatrick Venture }
82d8012181SPatrick Venture 
setManualMode(bool mode)83597ebd67SPatrick Venture void DbusPidZone::setManualMode(bool mode)
84d8012181SPatrick Venture {
85d8012181SPatrick Venture     _manualMode = mode;
86a4146eb1SJosh Lehan 
87a4146eb1SJosh Lehan     // If returning to automatic mode, need to restore PWM from PID loop
88a4146eb1SJosh Lehan     if (!mode)
89a4146eb1SJosh Lehan     {
90a4146eb1SJosh Lehan         _redundantWrite = true;
91a4146eb1SJosh Lehan     }
92d8012181SPatrick Venture }
93d8012181SPatrick Venture 
getFailSafeMode(void) const94597ebd67SPatrick Venture bool DbusPidZone::getFailSafeMode(void) const
95d8012181SPatrick Venture {
96d8012181SPatrick Venture     // If any keys are present at least one sensor is in fail safe mode.
97d8012181SPatrick Venture     return !_failSafeSensors.empty();
98d8012181SPatrick Venture }
99d8012181SPatrick Venture 
markSensorMissing(const std::string & name)1003f0f7bc3SJosh Lehan void DbusPidZone::markSensorMissing(const std::string& name)
1013f0f7bc3SJosh Lehan {
1023f0f7bc3SJosh Lehan     if (_missingAcceptable.find(name) != _missingAcceptable.end())
1033f0f7bc3SJosh Lehan     {
1043f0f7bc3SJosh Lehan         // Disallow sensors in MissingIsAcceptable list from causing failsafe
105*6df8bb50SJames Zheng         outputFailsafeLogWithZone(_zoneId, this->getFailSafeMode(), name,
106*6df8bb50SJames Zheng                                   "The sensor is missing but is acceptable.");
1073f0f7bc3SJosh Lehan         return;
1083f0f7bc3SJosh Lehan     }
1093f0f7bc3SJosh Lehan 
11092f9f3c8SHarvey Wu     if (_sensorFailSafePercent[name] == 0)
11192f9f3c8SHarvey Wu     {
11292f9f3c8SHarvey Wu         _failSafeSensors[name] = _zoneFailSafePercent;
11392f9f3c8SHarvey Wu     }
11492f9f3c8SHarvey Wu     else
11592f9f3c8SHarvey Wu     {
11692f9f3c8SHarvey Wu         _failSafeSensors[name] = _sensorFailSafePercent[name];
11792f9f3c8SHarvey Wu     }
11892f9f3c8SHarvey Wu 
11992f9f3c8SHarvey Wu     if (debugEnabled)
12092f9f3c8SHarvey Wu     {
12192f9f3c8SHarvey Wu         std::cerr << "Sensor " << name << " marked missing\n";
12292f9f3c8SHarvey Wu     }
1233f0f7bc3SJosh Lehan }
1243f0f7bc3SJosh Lehan 
getZoneID(void) const125597ebd67SPatrick Venture int64_t DbusPidZone::getZoneID(void) const
126d8012181SPatrick Venture {
127d8012181SPatrick Venture     return _zoneId;
128d8012181SPatrick Venture }
129d8012181SPatrick Venture 
addSetPoint(double setPoint,const std::string & name)130ccc8bb62SNirav Shah void DbusPidZone::addSetPoint(double setPoint, const std::string& name)
131d8012181SPatrick Venture {
1327c6d35d5Sykchiu     /* exclude disabled pidloop from _maximumSetPoint calculation*/
1337c6d35d5Sykchiu     if (!isPidProcessEnabled(name))
1347c6d35d5Sykchiu     {
1357c6d35d5Sykchiu         return;
1367c6d35d5Sykchiu     }
1377c6d35d5Sykchiu 
1389788963cSDelphine CC Chiu     auto profileName = name;
1399788963cSDelphine CC Chiu     if (getAccSetPoint())
1409788963cSDelphine CC Chiu     {
1419788963cSDelphine CC Chiu         /*
1429788963cSDelphine CC Chiu          * If the name of controller is Linear_Temp_CPU0.
1439788963cSDelphine CC Chiu          * The profile name will be Temp_CPU0.
1449788963cSDelphine CC Chiu          */
1459788963cSDelphine CC Chiu         profileName = name.substr(name.find("_") + 1);
1469788963cSDelphine CC Chiu         _SetPoints[profileName] += setPoint;
1479788963cSDelphine CC Chiu     }
1489788963cSDelphine CC Chiu     else
1499788963cSDelphine CC Chiu     {
1509788963cSDelphine CC Chiu         if (_SetPoints[profileName] < setPoint)
1519788963cSDelphine CC Chiu         {
1529788963cSDelphine CC Chiu             _SetPoints[profileName] = setPoint;
1539788963cSDelphine CC Chiu         }
1549788963cSDelphine CC Chiu     }
1559788963cSDelphine CC Chiu 
156ccc8bb62SNirav Shah     /*
157ccc8bb62SNirav Shah      * if there are multiple thermal controllers with the same
158ccc8bb62SNirav Shah      * value, pick the first one in the iterator
159ccc8bb62SNirav Shah      */
1609788963cSDelphine CC Chiu     if (_maximumSetPoint < _SetPoints[profileName])
161ccc8bb62SNirav Shah     {
1629788963cSDelphine CC Chiu         _maximumSetPoint = _SetPoints[profileName];
1639788963cSDelphine CC Chiu         _maximumSetPointName = profileName;
164ccc8bb62SNirav Shah     }
165d8012181SPatrick Venture }
166d8012181SPatrick Venture 
addRPMCeiling(double ceiling)167597ebd67SPatrick Venture void DbusPidZone::addRPMCeiling(double ceiling)
168608304daSJames Feist {
169608304daSJames Feist     _RPMCeilings.push_back(ceiling);
170608304daSJames Feist }
171608304daSJames Feist 
clearRPMCeilings(void)172597ebd67SPatrick Venture void DbusPidZone::clearRPMCeilings(void)
173608304daSJames Feist {
174608304daSJames Feist     _RPMCeilings.clear();
175608304daSJames Feist }
176608304daSJames Feist 
clearSetPoints(void)177597ebd67SPatrick Venture void DbusPidZone::clearSetPoints(void)
178d8012181SPatrick Venture {
1799bbf333dSPatrick Venture     _SetPoints.clear();
180ccc8bb62SNirav Shah     _maximumSetPoint = 0;
1817c6d35d5Sykchiu     _maximumSetPointName.clear();
182d8012181SPatrick Venture }
183d8012181SPatrick Venture 
getFailSafePercent(void)18492f9f3c8SHarvey Wu double DbusPidZone::getFailSafePercent(void)
185d8012181SPatrick Venture {
18692f9f3c8SHarvey Wu     std::map<std::string, double>::iterator maxData = std::max_element(
18792f9f3c8SHarvey Wu         _failSafeSensors.begin(), _failSafeSensors.end(),
18892f9f3c8SHarvey Wu         [](const std::pair<std::string, double> firstData,
18992f9f3c8SHarvey Wu            const std::pair<std::string, double> secondData) {
19092f9f3c8SHarvey Wu             return firstData.second < secondData.second;
19192f9f3c8SHarvey Wu         });
19292f9f3c8SHarvey Wu 
19392f9f3c8SHarvey Wu     // In dbus/dbusconfiguration.cpp, the default sensor failsafepercent is 0 if
19492f9f3c8SHarvey Wu     // there is no setting in json.
19592f9f3c8SHarvey Wu     // Therfore, if the max failsafe duty in _failSafeSensors is 0, set final
19692f9f3c8SHarvey Wu     // failsafe duty to _zoneFailSafePercent.
19792f9f3c8SHarvey Wu     if ((*maxData).second == 0)
19892f9f3c8SHarvey Wu     {
19992f9f3c8SHarvey Wu         return _zoneFailSafePercent;
20092f9f3c8SHarvey Wu     }
20192f9f3c8SHarvey Wu     else
20292f9f3c8SHarvey Wu     {
20392f9f3c8SHarvey Wu         return (*maxData).second;
20492f9f3c8SHarvey Wu     }
205d8012181SPatrick Venture }
206d8012181SPatrick Venture 
getMinThermalSetPoint(void) const207ccc8bb62SNirav Shah double DbusPidZone::getMinThermalSetPoint(void) const
208d8012181SPatrick Venture {
2093484bedaSJames Feist     return _minThermalOutputSetPt;
210d8012181SPatrick Venture }
211d8012181SPatrick Venture 
getCycleIntervalTime(void) const2120e8fc398SBonnie Lo uint64_t DbusPidZone::getCycleIntervalTime(void) const
2130e8fc398SBonnie Lo {
2140e8fc398SBonnie Lo     return _cycleTime.cycleIntervalTimeMS;
2150e8fc398SBonnie Lo }
2160e8fc398SBonnie Lo 
getUpdateThermalsCycle(void) const2170e8fc398SBonnie Lo uint64_t DbusPidZone::getUpdateThermalsCycle(void) const
2180e8fc398SBonnie Lo {
2190e8fc398SBonnie Lo     return _cycleTime.updateThermalsTimeMS;
2200e8fc398SBonnie Lo }
2210e8fc398SBonnie Lo 
addFanPID(std::unique_ptr<Controller> pid)222597ebd67SPatrick Venture void DbusPidZone::addFanPID(std::unique_ptr<Controller> pid)
223d8012181SPatrick Venture {
224d8012181SPatrick Venture     _fans.push_back(std::move(pid));
225d8012181SPatrick Venture }
226d8012181SPatrick Venture 
addThermalPID(std::unique_ptr<Controller> pid)227597ebd67SPatrick Venture void DbusPidZone::addThermalPID(std::unique_ptr<Controller> pid)
228d8012181SPatrick Venture {
229d8012181SPatrick Venture     _thermals.push_back(std::move(pid));
230d8012181SPatrick Venture }
231d8012181SPatrick Venture 
getCachedValue(const std::string & name)232597ebd67SPatrick Venture double DbusPidZone::getCachedValue(const std::string& name)
233d8012181SPatrick Venture {
234b300575eSJosh Lehan     return _cachedValuesByName.at(name).scaled;
235b300575eSJosh Lehan }
236b300575eSJosh Lehan 
getCachedValues(const std::string & name)237b300575eSJosh Lehan ValueCacheEntry DbusPidZone::getCachedValues(const std::string& name)
238b300575eSJosh Lehan {
239d8012181SPatrick Venture     return _cachedValuesByName.at(name);
240d8012181SPatrick Venture }
241d8012181SPatrick Venture 
setOutputCache(std::string_view name,const ValueCacheEntry & values)242b300575eSJosh Lehan void DbusPidZone::setOutputCache(std::string_view name,
243b300575eSJosh Lehan                                  const ValueCacheEntry& values)
244b300575eSJosh Lehan {
245b300575eSJosh Lehan     _cachedFanOutputs[std::string{name}] = values;
246b300575eSJosh Lehan }
247b300575eSJosh Lehan 
addFanInput(const std::string & fan,bool missingAcceptable)2483f0f7bc3SJosh Lehan void DbusPidZone::addFanInput(const std::string& fan, bool missingAcceptable)
249d8012181SPatrick Venture {
250d8012181SPatrick Venture     _fanInputs.push_back(fan);
2513f0f7bc3SJosh Lehan 
2523f0f7bc3SJosh Lehan     if (missingAcceptable)
2533f0f7bc3SJosh Lehan     {
2543f0f7bc3SJosh Lehan         _missingAcceptable.emplace(fan);
2553f0f7bc3SJosh Lehan     }
256d8012181SPatrick Venture }
257d8012181SPatrick Venture 
addThermalInput(const std::string & therm,bool missingAcceptable)2583f0f7bc3SJosh Lehan void DbusPidZone::addThermalInput(const std::string& therm,
2593f0f7bc3SJosh Lehan                                   bool missingAcceptable)
260d8012181SPatrick Venture {
2619788963cSDelphine CC Chiu     /*
2629788963cSDelphine CC Chiu      * One sensor may have stepwise and PID at the same time.
2639788963cSDelphine CC Chiu      * Searching the sensor name before inserting it to avoid duplicated sensor
2649788963cSDelphine CC Chiu      * names.
2659788963cSDelphine CC Chiu      */
2669788963cSDelphine CC Chiu     if (std::find(_thermalInputs.begin(), _thermalInputs.end(), therm) ==
2679788963cSDelphine CC Chiu         _thermalInputs.end())
2689788963cSDelphine CC Chiu     {
269d8012181SPatrick Venture         _thermalInputs.push_back(therm);
2709788963cSDelphine CC Chiu     }
2713f0f7bc3SJosh Lehan 
2723f0f7bc3SJosh Lehan     if (missingAcceptable)
2733f0f7bc3SJosh Lehan     {
2743f0f7bc3SJosh Lehan         _missingAcceptable.emplace(therm);
2753f0f7bc3SJosh Lehan     }
276d8012181SPatrick Venture }
277d8012181SPatrick Venture 
27855ccad65SJosh Lehan // Updates desired RPM setpoint from optional text file
27955ccad65SJosh Lehan // Returns true if rpmValue updated, false if left unchanged
fileParseRpm(const std::string & fileName,double & rpmValue)28055ccad65SJosh Lehan static bool fileParseRpm(const std::string& fileName, double& rpmValue)
28155ccad65SJosh Lehan {
28255ccad65SJosh Lehan     static constexpr std::chrono::seconds throttlePace{3};
28355ccad65SJosh Lehan 
28455ccad65SJosh Lehan     std::string errText;
28555ccad65SJosh Lehan 
28655ccad65SJosh Lehan     try
28755ccad65SJosh Lehan     {
28855ccad65SJosh Lehan         std::ifstream ifs;
28955ccad65SJosh Lehan         ifs.open(fileName);
29055ccad65SJosh Lehan         if (ifs)
29155ccad65SJosh Lehan         {
29255ccad65SJosh Lehan             int value;
29355ccad65SJosh Lehan             ifs >> value;
29455ccad65SJosh Lehan 
29555ccad65SJosh Lehan             if (value <= 0)
29655ccad65SJosh Lehan             {
29755ccad65SJosh Lehan                 errText = "File content could not be parsed to a number";
29855ccad65SJosh Lehan             }
29955ccad65SJosh Lehan             else if (value <= 100)
30055ccad65SJosh Lehan             {
30155ccad65SJosh Lehan                 errText = "File must contain RPM value, not PWM value";
30255ccad65SJosh Lehan             }
30355ccad65SJosh Lehan             else
30455ccad65SJosh Lehan             {
30555ccad65SJosh Lehan                 rpmValue = static_cast<double>(value);
30655ccad65SJosh Lehan                 return true;
30755ccad65SJosh Lehan             }
30855ccad65SJosh Lehan         }
30955ccad65SJosh Lehan     }
31055ccad65SJosh Lehan     catch (const std::exception& e)
31155ccad65SJosh Lehan     {
31255ccad65SJosh Lehan         errText = "Exception: ";
31355ccad65SJosh Lehan         errText += e.what();
31455ccad65SJosh Lehan     }
31555ccad65SJosh Lehan 
31655ccad65SJosh Lehan     // The file is optional, intentionally not an error if file not found
31755ccad65SJosh Lehan     if (!(errText.empty()))
31855ccad65SJosh Lehan     {
31955ccad65SJosh Lehan         tstamp now = std::chrono::high_resolution_clock::now();
32055ccad65SJosh Lehan         if (allowThrottle(now, throttlePace))
32155ccad65SJosh Lehan         {
32255ccad65SJosh Lehan             std::cerr << "Unable to read from '" << fileName << "': " << errText
32355ccad65SJosh Lehan                       << "\n";
32455ccad65SJosh Lehan         }
32555ccad65SJosh Lehan     }
32655ccad65SJosh Lehan 
32755ccad65SJosh Lehan     return false;
32855ccad65SJosh Lehan }
32955ccad65SJosh Lehan 
determineMaxSetPointRequest(void)330597ebd67SPatrick Venture void DbusPidZone::determineMaxSetPointRequest(void)
331d8012181SPatrick Venture {
3325f59c0fdSPatrick Venture     std::vector<double>::iterator result;
333ccc8bb62SNirav Shah     double minThermalThreshold = getMinThermalSetPoint();
334d8012181SPatrick Venture 
335608304daSJames Feist     if (_RPMCeilings.size() > 0)
336608304daSJames Feist     {
337608304daSJames Feist         result = std::min_element(_RPMCeilings.begin(), _RPMCeilings.end());
338ccc8bb62SNirav Shah         // if Max set point is larger than the lowest ceiling, reset to lowest
339ccc8bb62SNirav Shah         // ceiling.
340ccc8bb62SNirav Shah         if (*result < _maximumSetPoint)
341ccc8bb62SNirav Shah         {
342ccc8bb62SNirav Shah             _maximumSetPoint = *result;
343ccc8bb62SNirav Shah             // When using lowest ceiling, controller name is ceiling.
344ccc8bb62SNirav Shah             _maximumSetPointName = "Ceiling";
345ccc8bb62SNirav Shah         }
346608304daSJames Feist     }
347608304daSJames Feist 
348d8012181SPatrick Venture     /*
3499788963cSDelphine CC Chiu      * Combine the maximum SetPoint Name if the controllers have same profile
3509788963cSDelphine CC Chiu      * name. e.g., PID_BB_INLET_TEMP_C + Stepwise_BB_INLET_TEMP_C.
3519788963cSDelphine CC Chiu      */
3529788963cSDelphine CC Chiu     if (getAccSetPoint())
3539788963cSDelphine CC Chiu     {
3549788963cSDelphine CC Chiu         auto profileName = _maximumSetPointName;
3559788963cSDelphine CC Chiu         _maximumSetPointName = "";
3569788963cSDelphine CC Chiu 
3579788963cSDelphine CC Chiu         for (auto& p : _thermals)
3589788963cSDelphine CC Chiu         {
3599788963cSDelphine CC Chiu             auto controllerID = p->getID();
3609788963cSDelphine CC Chiu             auto found = controllerID.find(profileName);
3619788963cSDelphine CC Chiu             if (found != std::string::npos)
3629788963cSDelphine CC Chiu             {
3639788963cSDelphine CC Chiu                 if (_maximumSetPointName.empty())
3649788963cSDelphine CC Chiu                 {
3659788963cSDelphine CC Chiu                     _maximumSetPointName = controllerID;
3669788963cSDelphine CC Chiu                 }
3679788963cSDelphine CC Chiu                 else
3689788963cSDelphine CC Chiu                 {
3699788963cSDelphine CC Chiu                     _maximumSetPointName += " + " + controllerID;
3709788963cSDelphine CC Chiu                 }
3719788963cSDelphine CC Chiu             }
3729788963cSDelphine CC Chiu         }
3739788963cSDelphine CC Chiu     }
3749788963cSDelphine CC Chiu 
3759788963cSDelphine CC Chiu     /*
3767280e27eSPatrick Venture      * If the maximum RPM setpoint output is below the minimum RPM
3777280e27eSPatrick Venture      * setpoint, set it to the minimum.
378d8012181SPatrick Venture      */
379ccc8bb62SNirav Shah     if (minThermalThreshold >= _maximumSetPoint)
380ccc8bb62SNirav Shah     {
381ccc8bb62SNirav Shah         _maximumSetPoint = minThermalThreshold;
3827c6d35d5Sykchiu         _maximumSetPointName = "Minimum";
383ccc8bb62SNirav Shah     }
384ccc8bb62SNirav Shah     else if (_maximumSetPointName.compare(_maximumSetPointNamePrev))
385ccc8bb62SNirav Shah     {
386ccc8bb62SNirav Shah         std::cerr << "PID Zone " << _zoneId << " max SetPoint "
387ccc8bb62SNirav Shah                   << _maximumSetPoint << " requested by "
388ccc8bb62SNirav Shah                   << _maximumSetPointName;
389ccc8bb62SNirav Shah         for (const auto& sensor : _failSafeSensors)
390ccc8bb62SNirav Shah         {
39192f9f3c8SHarvey Wu             if (sensor.first.find("Fan") == std::string::npos)
392ccc8bb62SNirav Shah             {
39392f9f3c8SHarvey Wu                 std::cerr << " " << sensor.first;
394ccc8bb62SNirav Shah             }
395ccc8bb62SNirav Shah         }
396ccc8bb62SNirav Shah         std::cerr << "\n";
397ccc8bb62SNirav Shah         _maximumSetPointNamePrev.assign(_maximumSetPointName);
398ccc8bb62SNirav Shah     }
399de79ee05SPatrick Venture     if (tuningEnabled)
400c32e3fc5SPatrick Venture     {
401d8012181SPatrick Venture         /*
4027280e27eSPatrick Venture          * We received no setpoints from thermal sensors.
403d8012181SPatrick Venture          * This is a case experienced during tuning where they only specify
404d8012181SPatrick Venture          * fan sensors and one large fan PID for all the fans.
405d8012181SPatrick Venture          */
4067280e27eSPatrick Venture         static constexpr auto setpointpath = "/etc/thermal.d/setpoint";
407df766f25SPatrick Venture 
408ccc8bb62SNirav Shah         fileParseRpm(setpointpath, _maximumSetPoint);
40955ccad65SJosh Lehan 
41055ccad65SJosh Lehan         // Allow per-zone setpoint files to override overall setpoint file
41155ccad65SJosh Lehan         std::ostringstream zoneSuffix;
41255ccad65SJosh Lehan         zoneSuffix << ".zone" << _zoneId;
41355ccad65SJosh Lehan         std::string zoneSetpointPath = setpointpath + zoneSuffix.str();
41455ccad65SJosh Lehan 
415ccc8bb62SNirav Shah         fileParseRpm(zoneSetpointPath, _maximumSetPoint);
416c32e3fc5SPatrick Venture     }
417d8012181SPatrick Venture     return;
418d8012181SPatrick Venture }
419d8012181SPatrick Venture 
initializeLog(void)420597ebd67SPatrick Venture void DbusPidZone::initializeLog(void)
421d8012181SPatrick Venture {
4225f02ad28SPatrick Venture     /* Print header for log file:
423b300575eSJosh Lehan      * epoch_ms,setpt,fan1,fan1_raw,fan1_pwm,fan1_pwm_raw,fan2,fan2_raw,fan2_pwm,fan2_pwm_raw,fanN,fanN_raw,fanN_pwm,fanN_pwm_raw,sensor1,sensor1_raw,sensor2,sensor2_raw,sensorN,sensorN_raw,failsafe
4245f02ad28SPatrick Venture      */
425d8012181SPatrick Venture 
426ccc8bb62SNirav Shah     _log << "epoch_ms,setpt,requester";
427d8012181SPatrick Venture 
4284a2dc4d8SPatrick Venture     for (const auto& f : _fanInputs)
429d8012181SPatrick Venture     {
430b300575eSJosh Lehan         _log << "," << f << "," << f << "_raw";
431b300575eSJosh Lehan         _log << "," << f << "_pwm," << f << "_pwm_raw";
432d8012181SPatrick Venture     }
4334a2dc4d8SPatrick Venture     for (const auto& t : _thermalInputs)
4345f02ad28SPatrick Venture     {
435b300575eSJosh Lehan         _log << "," << t << "," << t << "_raw";
4365f02ad28SPatrick Venture     }
437b300575eSJosh Lehan 
4385f02ad28SPatrick Venture     _log << ",failsafe";
439d8012181SPatrick Venture     _log << std::endl;
440d8012181SPatrick Venture }
441d8012181SPatrick Venture 
writeLog(const std::string & value)4427a98c19aSPatrick Venture void DbusPidZone::writeLog(const std::string& value)
443d8012181SPatrick Venture {
4447a98c19aSPatrick Venture     _log << value;
445d8012181SPatrick Venture }
446d8012181SPatrick Venture 
447d8012181SPatrick Venture /*
448d8012181SPatrick Venture  * TODO(venture) This is effectively updating the cache and should check if the
449d8012181SPatrick Venture  * values they're using to update it are new or old, or whatnot.  For instance,
450d8012181SPatrick Venture  * if we haven't heard from the host in X time we need to detect this failure.
451d8012181SPatrick Venture  *
452d8012181SPatrick Venture  * I haven't decided if the Sensor should have a lastUpdated method or whether
453d8012181SPatrick Venture  * that should be for the ReadInterface or etc...
454d8012181SPatrick Venture  */
455d8012181SPatrick Venture 
456d8012181SPatrick Venture /**
457d8012181SPatrick Venture  * We want the PID loop to run with values cached, so this will get all the
458d8012181SPatrick Venture  * fan tachs for the loop.
459d8012181SPatrick Venture  */
updateFanTelemetry(void)460597ebd67SPatrick Venture void DbusPidZone::updateFanTelemetry(void)
461d8012181SPatrick Venture {
462d8012181SPatrick Venture     /* TODO(venture): Should I just make _log point to /dev/null when logging
463d8012181SPatrick Venture      * is disabled?  I think it's a waste to try and log things even if the
464d8012181SPatrick Venture      * data is just being dropped though.
465d8012181SPatrick Venture      */
466df1f183fSTom Tung     const auto now = std::chrono::high_resolution_clock::now();
467de79ee05SPatrick Venture     if (loggingEnabled)
468c32e3fc5SPatrick Venture     {
469da4a5dd1SPatrick Venture         _log << std::chrono::duration_cast<std::chrono::milliseconds>(
470da4a5dd1SPatrick Venture                     now.time_since_epoch())
471da4a5dd1SPatrick Venture                     .count();
472f7a2dd5cSPatrick Venture         _log << "," << _maximumSetPoint;
473ccc8bb62SNirav Shah         _log << "," << _maximumSetPointName;
474c32e3fc5SPatrick Venture     }
475d8012181SPatrick Venture 
476df1f183fSTom Tung     processSensorInputs</* fanSensorLogging */ true>(_fanInputs, now);
477d8012181SPatrick Venture 
478de79ee05SPatrick Venture     if (loggingEnabled)
479c32e3fc5SPatrick Venture     {
4804a2dc4d8SPatrick Venture         for (const auto& t : _thermalInputs)
4815f02ad28SPatrick Venture         {
482b300575eSJosh Lehan             const auto& v = _cachedValuesByName[t];
483b300575eSJosh Lehan             _log << "," << v.scaled << "," << v.unscaled;
4845f02ad28SPatrick Venture         }
485c32e3fc5SPatrick Venture     }
4865f02ad28SPatrick Venture 
487d8012181SPatrick Venture     return;
488d8012181SPatrick Venture }
489d8012181SPatrick Venture 
updateSensors(void)490597ebd67SPatrick Venture void DbusPidZone::updateSensors(void)
491d8012181SPatrick Venture {
492df1f183fSTom Tung     processSensorInputs</* fanSensorLogging */ false>(
493df1f183fSTom Tung         _thermalInputs, std::chrono::high_resolution_clock::now());
494d8012181SPatrick Venture 
495d8012181SPatrick Venture     return;
496d8012181SPatrick Venture }
497d8012181SPatrick Venture 
initializeCache(void)498597ebd67SPatrick Venture void DbusPidZone::initializeCache(void)
499d8012181SPatrick Venture {
5003f0f7bc3SJosh Lehan     auto nan = std::numeric_limits<double>::quiet_NaN();
5013f0f7bc3SJosh Lehan 
5024a2dc4d8SPatrick Venture     for (const auto& f : _fanInputs)
503d8012181SPatrick Venture     {
5043f0f7bc3SJosh Lehan         _cachedValuesByName[f] = {nan, nan};
5053f0f7bc3SJosh Lehan         _cachedFanOutputs[f] = {nan, nan};
506ded0ab56SWill Liang 
507ded0ab56SWill Liang         // Start all fans in fail-safe mode.
5083f0f7bc3SJosh Lehan         markSensorMissing(f);
509d8012181SPatrick Venture     }
510d8012181SPatrick Venture 
5114a2dc4d8SPatrick Venture     for (const auto& t : _thermalInputs)
512d8012181SPatrick Venture     {
5133f0f7bc3SJosh Lehan         _cachedValuesByName[t] = {nan, nan};
514d8012181SPatrick Venture 
515d8012181SPatrick Venture         // Start all sensors in fail-safe mode.
5163f0f7bc3SJosh Lehan         markSensorMissing(t);
517d8012181SPatrick Venture     }
518d8012181SPatrick Venture }
519d8012181SPatrick Venture 
dumpCache(void)520597ebd67SPatrick Venture void DbusPidZone::dumpCache(void)
521d8012181SPatrick Venture {
522d8012181SPatrick Venture     std::cerr << "Cache values now: \n";
5232a50eda8SPatrick Venture     for (const auto& [name, value] : _cachedValuesByName)
524d8012181SPatrick Venture     {
525b300575eSJosh Lehan         std::cerr << name << ": " << value.scaled << " " << value.unscaled
526b300575eSJosh Lehan                   << "\n";
527b300575eSJosh Lehan     }
528b300575eSJosh Lehan 
529b300575eSJosh Lehan     std::cerr << "Fan outputs now: \n";
530b300575eSJosh Lehan     for (const auto& [name, value] : _cachedFanOutputs)
531b300575eSJosh Lehan     {
532b300575eSJosh Lehan         std::cerr << name << ": " << value.scaled << " " << value.unscaled
533b300575eSJosh Lehan                   << "\n";
534d8012181SPatrick Venture     }
535d8012181SPatrick Venture }
536d8012181SPatrick Venture 
processFans(void)537597ebd67SPatrick Venture void DbusPidZone::processFans(void)
538d8012181SPatrick Venture {
539d8012181SPatrick Venture     for (auto& p : _fans)
540d8012181SPatrick Venture     {
54122c257abSJames Feist         p->process();
542d8012181SPatrick Venture     }
543a4146eb1SJosh Lehan 
544a4146eb1SJosh Lehan     if (_redundantWrite)
545a4146eb1SJosh Lehan     {
546a4146eb1SJosh Lehan         // This is only needed once
547a4146eb1SJosh Lehan         _redundantWrite = false;
548a4146eb1SJosh Lehan     }
549d8012181SPatrick Venture }
550d8012181SPatrick Venture 
processThermals(void)551597ebd67SPatrick Venture void DbusPidZone::processThermals(void)
552d8012181SPatrick Venture {
553d8012181SPatrick Venture     for (auto& p : _thermals)
554d8012181SPatrick Venture     {
55522c257abSJames Feist         p->process();
556d8012181SPatrick Venture     }
557d8012181SPatrick Venture }
558d8012181SPatrick Venture 
getSensor(const std::string & name)559597ebd67SPatrick Venture Sensor* DbusPidZone::getSensor(const std::string& name)
560d8012181SPatrick Venture {
561fe75b193SPatrick Venture     return _mgr.getSensor(name);
562d8012181SPatrick Venture }
563d8012181SPatrick Venture 
getSensorNames(void)564*6df8bb50SJames Zheng std::vector<std::string> DbusPidZone::getSensorNames(void)
565*6df8bb50SJames Zheng {
566*6df8bb50SJames Zheng     return _thermalInputs;
567*6df8bb50SJames Zheng }
568*6df8bb50SJames Zheng 
getRedundantWrite(void) const569a4146eb1SJosh Lehan bool DbusPidZone::getRedundantWrite(void) const
570a4146eb1SJosh Lehan {
571a4146eb1SJosh Lehan     return _redundantWrite;
572a4146eb1SJosh Lehan }
573a4146eb1SJosh Lehan 
manual(bool value)574597ebd67SPatrick Venture bool DbusPidZone::manual(bool value)
575d8012181SPatrick Venture {
576d8012181SPatrick Venture     std::cerr << "manual: " << value << std::endl;
577d8012181SPatrick Venture     setManualMode(value);
578d8012181SPatrick Venture     return ModeObject::manual(value);
579d8012181SPatrick Venture }
580d8012181SPatrick Venture 
failSafe() const581597ebd67SPatrick Venture bool DbusPidZone::failSafe() const
582d8012181SPatrick Venture {
583d8012181SPatrick Venture     return getFailSafeMode();
584d8012181SPatrick Venture }
585a076487aSPatrick Venture 
addPidControlProcess(std::string name,std::string type,double setpoint,sdbusplus::bus_t & bus,std::string objPath,bool defer)58637180062SHarvey Wu void DbusPidZone::addPidControlProcess(std::string name, std::string type,
58737180062SHarvey Wu                                        double setpoint, sdbusplus::bus_t& bus,
5887c6d35d5Sykchiu                                        std::string objPath, bool defer)
5897c6d35d5Sykchiu {
5907c6d35d5Sykchiu     _pidsControlProcess[name] = std::make_unique<ProcessObject>(
5917c6d35d5Sykchiu         bus, objPath.c_str(),
5927c6d35d5Sykchiu         defer ? ProcessObject::action::defer_emit
5937c6d35d5Sykchiu               : ProcessObject::action::emit_object_added);
5947c6d35d5Sykchiu     // Default enable setting = true
5957c6d35d5Sykchiu     _pidsControlProcess[name]->enabled(true);
59637180062SHarvey Wu     _pidsControlProcess[name]->setpoint(setpoint);
59737180062SHarvey Wu 
59837180062SHarvey Wu     if (type == "temp")
59937180062SHarvey Wu     {
60037180062SHarvey Wu         _pidsControlProcess[name]->classType("Temperature");
60137180062SHarvey Wu     }
60237180062SHarvey Wu     else if (type == "margin")
60337180062SHarvey Wu     {
60437180062SHarvey Wu         _pidsControlProcess[name]->classType("Margin");
60537180062SHarvey Wu     }
60637180062SHarvey Wu     else if (type == "power")
60737180062SHarvey Wu     {
60837180062SHarvey Wu         _pidsControlProcess[name]->classType("Power");
60937180062SHarvey Wu     }
61037180062SHarvey Wu     else if (type == "powersum")
61137180062SHarvey Wu     {
61237180062SHarvey Wu         _pidsControlProcess[name]->classType("PowerSum");
61337180062SHarvey Wu     }
6147c6d35d5Sykchiu }
6157c6d35d5Sykchiu 
isPidProcessEnabled(std::string name)6167c6d35d5Sykchiu bool DbusPidZone::isPidProcessEnabled(std::string name)
6177c6d35d5Sykchiu {
6187c6d35d5Sykchiu     return _pidsControlProcess[name]->enabled();
6197c6d35d5Sykchiu }
6207c6d35d5Sykchiu 
addPidFailSafePercent(std::vector<std::string> inputs,double percent)62192f9f3c8SHarvey Wu void DbusPidZone::addPidFailSafePercent(std::vector<std::string> inputs,
62292f9f3c8SHarvey Wu                                         double percent)
6239fe3a3c7Sykchiu {
62492f9f3c8SHarvey Wu     for (const auto& sensorName : inputs)
6259fe3a3c7Sykchiu     {
62692f9f3c8SHarvey Wu         if (_sensorFailSafePercent.find(sensorName) !=
62792f9f3c8SHarvey Wu             _sensorFailSafePercent.end())
62892f9f3c8SHarvey Wu         {
62992f9f3c8SHarvey Wu             _sensorFailSafePercent[sensorName] =
63092f9f3c8SHarvey Wu                 std::max(_sensorFailSafePercent[sensorName], percent);
63192f9f3c8SHarvey Wu             if (debugEnabled)
63292f9f3c8SHarvey Wu             {
63392f9f3c8SHarvey Wu                 std::cerr << "Sensor " << sensorName
63492f9f3c8SHarvey Wu                           << " failsafe percent updated to "
63592f9f3c8SHarvey Wu                           << _sensorFailSafePercent[sensorName] << "\n";
6369fe3a3c7Sykchiu             }
6379fe3a3c7Sykchiu         }
63892f9f3c8SHarvey Wu         else
6399fe3a3c7Sykchiu         {
64092f9f3c8SHarvey Wu             _sensorFailSafePercent[sensorName] = percent;
64192f9f3c8SHarvey Wu             if (debugEnabled)
64292f9f3c8SHarvey Wu             {
64392f9f3c8SHarvey Wu                 std::cerr << "Sensor " << sensorName
64492f9f3c8SHarvey Wu                           << " failsafe percent set to " << percent << "\n";
64592f9f3c8SHarvey Wu             }
64692f9f3c8SHarvey Wu         }
64792f9f3c8SHarvey Wu     }
6489fe3a3c7Sykchiu }
6499fe3a3c7Sykchiu 
leader() const650cc0232afSHarvey Wu std::string DbusPidZone::leader() const
651cc0232afSHarvey Wu {
652cc0232afSHarvey Wu     return _maximumSetPointName;
653cc0232afSHarvey Wu }
654cc0232afSHarvey Wu 
updateThermalPowerDebugInterface(std::string pidName,std::string leader,double input,double output)655bd63bcacSPatrick Williams void DbusPidZone::updateThermalPowerDebugInterface(
656bd63bcacSPatrick Williams     std::string pidName, std::string leader, double input, double output)
65737180062SHarvey Wu {
65837180062SHarvey Wu     if (leader.empty())
65937180062SHarvey Wu     {
66037180062SHarvey Wu         _pidsControlProcess[pidName]->output(output);
66137180062SHarvey Wu     }
66237180062SHarvey Wu     else
66337180062SHarvey Wu     {
66437180062SHarvey Wu         _pidsControlProcess[pidName]->leader(leader);
66537180062SHarvey Wu         _pidsControlProcess[pidName]->input(input);
66637180062SHarvey Wu     }
66737180062SHarvey Wu }
66837180062SHarvey Wu 
getAccSetPoint(void) const6699788963cSDelphine CC Chiu bool DbusPidZone::getAccSetPoint(void) const
6709788963cSDelphine CC Chiu {
6719788963cSDelphine CC Chiu     return _accumulateSetPoint;
6729788963cSDelphine CC Chiu }
6739788963cSDelphine CC Chiu 
674a076487aSPatrick Venture } // namespace pid_control
675