PID Objects & Algo
These are the PID controller implementations for fans,
and thermals. This also includes the PID algorithm used.
Change-Id: I30471fbf7a8a7ed65f78bf105970d62815fedc56
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/pid/fancontroller.cpp b/pid/fancontroller.cpp
new file mode 100644
index 0000000..b5949a8
--- /dev/null
+++ b/pid/fancontroller.cpp
@@ -0,0 +1,146 @@
+/**
+ * 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 <algorithm>
+#include <iostream>
+
+#include "fancontroller.hpp"
+#include "util.hpp"
+#include "zone.hpp"
+
+std::unique_ptr<PIDController> FanController::CreateFanPid(
+ std::shared_ptr<PIDZone> owner,
+ const std::string& id,
+ std::vector<std::string>& inputs,
+ ec::pidinfo initial)
+{
+ auto fan = std::make_unique<FanController>(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<int64_t> values;
+ std::vector<int64_t>::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;
+ }
+
+ 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<float>(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<double>(percent));
+ }
+
+#ifdef __TUNING_LOGGING__
+ _owner->getLogHandle() << "," << percent;
+#endif
+
+ return;
+}
+