1 /**
2  * Copyright 2017 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "thermalcontroller.hpp"
18 
19 #include "errors/exception.hpp"
20 #include "tuning.hpp"
21 #include "util.hpp"
22 #include "zone.hpp"
23 
24 #include <algorithm>
25 #include <cmath>
26 #include <iostream>
27 
28 namespace pid_control
29 {
30 
31 ThermalType getThermalType(const std::string& typeString)
32 {
33     if (typeString == "margin")
34     {
35         return ThermalType::margin;
36     }
37     if ((typeString == "temp") || (typeString == "power"))
38     {
39         return ThermalType::absolute;
40     }
41     if (typeString == "powersum")
42     {
43         return ThermalType::summation;
44     }
45 
46     throw ControllerBuildException("Unrecognized PID Type/Class string");
47 }
48 
49 bool isThermalType(const std::string& typeString)
50 {
51     static const std::vector<std::string> thermalTypes = {"temp", "margin",
52                                                           "power", "powersum"};
53     return std::count(thermalTypes.begin(), thermalTypes.end(), typeString);
54 }
55 
56 std::unique_ptr<PIDController> ThermalController::createThermalPid(
57     ZoneInterface* owner, const std::string& id,
58     const std::vector<std::string>& inputs, double setpoint,
59     const ec::pidinfo& initial, const ThermalType& type)
60 {
61     // ThermalController requires at least 1 input
62     if (inputs.empty())
63     {
64         throw ControllerBuildException("Thermal controller missing inputs");
65     }
66 
67     auto thermal = std::make_unique<ThermalController>(id, inputs, type, owner);
68 
69     ec::pid_info_t* info = thermal->getPIDInfo();
70     thermal->setSetpoint(setpoint);
71 
72     initializePIDStruct(info, initial);
73 
74     return thermal;
75 }
76 
77 // bmc_host_sensor_value_double
78 double ThermalController::inputProc(void)
79 {
80     double value;
81     const double& (*compare)(const double&, const double&);
82     bool doSummation = false;
83 
84     if (type == ThermalType::margin)
85     {
86         value = std::numeric_limits<double>::max();
87         compare = std::min<double>;
88     }
89     else if (type == ThermalType::absolute)
90     {
91         value = std::numeric_limits<double>::lowest();
92         compare = std::max<double>;
93     }
94     else if (type == ThermalType::summation)
95     {
96         doSummation = true;
97         value = 0.0;
98     }
99     else
100     {
101         throw ControllerBuildException("Unrecognized ThermalType");
102     }
103 
104     bool acceptable = false;
105     for (const auto& in : _inputs)
106     {
107         double cachedValue = _owner->getCachedValue(in);
108 
109         // Less than 0 is perfectly OK for temperature, but must not be NAN
110         if (!(std::isfinite(cachedValue)))
111         {
112             continue;
113         }
114 
115         if (doSummation)
116         {
117             value += cachedValue;
118         }
119         else
120         {
121             value = compare(value, cachedValue);
122         }
123 
124         acceptable = true;
125     }
126 
127     if (!acceptable)
128     {
129         // While not optimal, zero is better than garbage
130         value = 0;
131     }
132 
133     if (debugEnabled)
134     {
135         std::cerr << getID() << " choose the temperature value: " << value
136                   << "\n";
137     }
138 
139     return value;
140 }
141 
142 // bmc_get_setpt
143 double ThermalController::setptProc(void)
144 {
145     double setpoint = getSetpoint();
146 
147     /* TODO(venture): Thermal setpoint invalid? */
148 #if 0
149     if (-1 == setpoint)
150     {
151         return 0.0f;
152     }
153     else
154     {
155         return setpoint;
156     }
157 #endif
158     return setpoint;
159 }
160 
161 // bmc_set_pid_output
162 void ThermalController::outputProc(double value)
163 {
164     _owner->addSetPoint(value, _id);
165 
166     if (debugEnabled)
167     {
168         std::cerr << getID() << " pid output pwm: " << value << "\n";
169     }
170 
171     return;
172 }
173 
174 } // namespace pid_control
175