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