/** * Copyright 2017 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "fancontroller.hpp" #include "util.hpp" #include "zone.hpp" std::unique_ptr FanController::CreateFanPid( ZoneInterface* owner, const std::string& id, std::vector& inputs, ec::pidinfo initial) { if (inputs.size() == 0) { return nullptr; } auto fan = std::make_unique(id, inputs, owner); ec::pid_info_t* info = fan->get_pid_info(); InitializePIDStruct(info, &initial); return fan; } float FanController::input_proc(void) { float sum = 0; double value = 0; std::vector values; std::vector::iterator result; try { for (const auto& name : _inputs) { value = _owner->getCachedValue(name); /* If we have a fan we can't read, its value will be 0 for at least * some boards, while others... the fan will drop off dbus (if * that's how it's being read and in that case its value will never * be updated anymore, which is relatively harmless, except, when * something tries to read its value through IPMI, and can't, they * sort of have to guess -- all the other fans are reporting, why * not this one? Maybe it's unable to be read, so it's "bad." */ if (value > 0) { values.push_back(value); sum += value; } } } catch (const std::exception& e) { std::cerr << "exception on input_proc.\n"; throw; } /* Reset the value from the above loop. */ value = 0; if (values.size() > 0) { /* When this is a configuration option in a later update, this code * will be updated. */ //value = sum / _inputs.size(); /* the fan PID algorithm was unstable with average, and seemed to work * better with minimum. I had considered making this choice a variable * in the configuration, and I will. */ result = std::min_element(values.begin(), values.end()); value = *result; } return static_cast(value); } float FanController::setpt_proc(void) { float maxRPM = _owner->getMaxRPMRequest(); // store for reference, and check if more or less. float prev = get_setpoint(); if (maxRPM > prev) { setFanDirection(FanSpeedDirection::UP); } else if (prev > maxRPM) { setFanDirection(FanSpeedDirection::DOWN); } else { setFanDirection(FanSpeedDirection::NEUTRAL); } set_setpoint(maxRPM); return (maxRPM); } void FanController::output_proc(float value) { float percent = value; /* If doing tuning logging, don't go into failsafe mode. */ #ifndef __TUNING_LOGGING__ if (_owner->getFailSafeMode()) { /* In case it's being set to 100% */ if (percent < _owner->getFailSafePercent()) { percent = _owner->getFailSafePercent(); } } #endif // value and kFanFailSafeDutyCycle are 10 for 10% so let's fix that. percent /= 100; // PidSensorMap for writing. for (auto& it : _inputs) { auto sensor = _owner->getSensor(it); sensor->write(static_cast(percent)); } return; }