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