1*ce6a3f36SJames Feist /** 2*ce6a3f36SJames Feist * Copyright 2017 Google Inc. 3*ce6a3f36SJames Feist * 4*ce6a3f36SJames Feist * Licensed under the Apache License, Version 2.0 (the "License"); 5*ce6a3f36SJames Feist * you may not use this file except in compliance with the License. 6*ce6a3f36SJames Feist * You may obtain a copy of the License at 7*ce6a3f36SJames Feist * 8*ce6a3f36SJames Feist * http://www.apache.org/licenses/LICENSE-2.0 9*ce6a3f36SJames Feist * 10*ce6a3f36SJames Feist * Unless required by applicable law or agreed to in writing, software 11*ce6a3f36SJames Feist * distributed under the License is distributed on an "AS IS" BASIS, 12*ce6a3f36SJames Feist * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*ce6a3f36SJames Feist * See the License for the specific language governing permissions and 14*ce6a3f36SJames Feist * limitations under the License. 15*ce6a3f36SJames Feist */ 16*ce6a3f36SJames Feist 17*ce6a3f36SJames Feist #include "pidloop.hpp" 18*ce6a3f36SJames Feist 19*ce6a3f36SJames Feist #include "pid/pidcontroller.hpp" 20*ce6a3f36SJames Feist #include "pid/tuning.hpp" 21*ce6a3f36SJames Feist #include "sensors/sensor.hpp" 22*ce6a3f36SJames Feist 23*ce6a3f36SJames Feist #include <boost/asio/steady_timer.hpp> 24*ce6a3f36SJames Feist #include <chrono> 25*ce6a3f36SJames Feist #include <map> 26*ce6a3f36SJames Feist #include <memory> 27*ce6a3f36SJames Feist #include <thread> 28*ce6a3f36SJames Feist #include <vector> 29*ce6a3f36SJames Feist 30*ce6a3f36SJames Feist static void processThermals(PIDZone* zone) 31*ce6a3f36SJames Feist { 32*ce6a3f36SJames Feist // Get the latest margins. 33*ce6a3f36SJames Feist zone->updateSensors(); 34*ce6a3f36SJames Feist // Zero out the RPM set point goals. 35*ce6a3f36SJames Feist zone->clearRPMSetPoints(); 36*ce6a3f36SJames Feist zone->clearRPMCeilings(); 37*ce6a3f36SJames Feist // Run the margin PIDs. 38*ce6a3f36SJames Feist zone->processThermals(); 39*ce6a3f36SJames Feist // Get the maximum RPM setpoint. 40*ce6a3f36SJames Feist zone->determineMaxRPMRequest(); 41*ce6a3f36SJames Feist } 42*ce6a3f36SJames Feist 43*ce6a3f36SJames Feist void pidControlLoop(PIDZone* zone, boost::asio::steady_timer& timer, bool first, 44*ce6a3f36SJames Feist int ms100cnt) 45*ce6a3f36SJames Feist { 46*ce6a3f36SJames Feist if (first) 47*ce6a3f36SJames Feist { 48*ce6a3f36SJames Feist if (tuningLoggingEnabled) 49*ce6a3f36SJames Feist { 50*ce6a3f36SJames Feist zone->initializeLog(); 51*ce6a3f36SJames Feist } 52*ce6a3f36SJames Feist 53*ce6a3f36SJames Feist zone->initializeCache(); 54*ce6a3f36SJames Feist processThermals(zone); 55*ce6a3f36SJames Feist } 56*ce6a3f36SJames Feist 57*ce6a3f36SJames Feist timer.expires_after(std::chrono::milliseconds(100)); 58*ce6a3f36SJames Feist timer.async_wait( 59*ce6a3f36SJames Feist [zone, &timer, ms100cnt](const boost::system::error_code& ec) mutable { 60*ce6a3f36SJames Feist /* 61*ce6a3f36SJames Feist * This should sleep on the conditional wait for the listen thread 62*ce6a3f36SJames Feist * to tell us it's in sync. But then we also need a timeout option 63*ce6a3f36SJames Feist * in case phosphor-hwmon is down, we can go into some weird failure 64*ce6a3f36SJames Feist * more. 65*ce6a3f36SJames Feist * 66*ce6a3f36SJames Feist * Another approach would be to start all sensors in worst-case 67*ce6a3f36SJames Feist * values, and fail-safe mode and then clear out of fail-safe mode 68*ce6a3f36SJames Feist * once we start getting values. Which I think it is a solid 69*ce6a3f36SJames Feist * approach. 70*ce6a3f36SJames Feist * 71*ce6a3f36SJames Feist * For now this runs before it necessarily has any sensor values. 72*ce6a3f36SJames Feist * For the host sensors they start out in fail-safe mode. For the 73*ce6a3f36SJames Feist * fans, they start out as 0 as input and then are adjusted once 74*ce6a3f36SJames Feist * they have values. 75*ce6a3f36SJames Feist * 76*ce6a3f36SJames Feist * If a fan has failed, it's value will be whatever we're told or 77*ce6a3f36SJames Feist * however we retrieve it. This program disregards fan values of 0, 78*ce6a3f36SJames Feist * so any code providing a fan speed can set to 0 on failure and 79*ce6a3f36SJames Feist * that fan value will be effectively ignored. The PID algorithm 80*ce6a3f36SJames Feist * will be unhappy but nothing bad will happen. 81*ce6a3f36SJames Feist * 82*ce6a3f36SJames Feist * TODO(venture): If the fan value is 0 should that loop just be 83*ce6a3f36SJames Feist * skipped? Right now, a 0 value is ignored in 84*ce6a3f36SJames Feist * FanController::inputProc() 85*ce6a3f36SJames Feist */ 86*ce6a3f36SJames Feist 87*ce6a3f36SJames Feist // Check if we should just go back to sleep. 88*ce6a3f36SJames Feist if (zone->getManualMode()) 89*ce6a3f36SJames Feist { 90*ce6a3f36SJames Feist pidControlLoop(zone, timer, false, ms100cnt); 91*ce6a3f36SJames Feist return; 92*ce6a3f36SJames Feist } 93*ce6a3f36SJames Feist 94*ce6a3f36SJames Feist // Get the latest fan speeds. 95*ce6a3f36SJames Feist zone->updateFanTelemetry(); 96*ce6a3f36SJames Feist 97*ce6a3f36SJames Feist if (10 <= ms100cnt) 98*ce6a3f36SJames Feist { 99*ce6a3f36SJames Feist ms100cnt = 0; 100*ce6a3f36SJames Feist 101*ce6a3f36SJames Feist processThermals(zone); 102*ce6a3f36SJames Feist } 103*ce6a3f36SJames Feist 104*ce6a3f36SJames Feist // Run the fan PIDs every iteration. 105*ce6a3f36SJames Feist zone->processFans(); 106*ce6a3f36SJames Feist 107*ce6a3f36SJames Feist if (tuningLoggingEnabled) 108*ce6a3f36SJames Feist { 109*ce6a3f36SJames Feist zone->getLogHandle() << "," << zone->getFailSafeMode(); 110*ce6a3f36SJames Feist zone->getLogHandle() << std::endl; 111*ce6a3f36SJames Feist } 112*ce6a3f36SJames Feist 113*ce6a3f36SJames Feist ms100cnt += 1; 114*ce6a3f36SJames Feist 115*ce6a3f36SJames Feist pidControlLoop(zone, timer, false, ms100cnt); 116*ce6a3f36SJames Feist }); 117*ce6a3f36SJames Feist } 118