blob: 8925e2e3fa6658e534909ddaa3804420dfdf5d8d [file] [log] [blame]
Alexander Hansen46a755f2025-10-27 16:31:08 +01001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright 2019 Google Inc
Patrick Ventured1491722019-02-08 14:37:45 -08003
4#include "pid/buildjson.hpp"
5
6#include "conf.hpp"
Ed Tanousf8b6e552025-06-27 13:27:50 -07007#include "ec/stepwise.hpp"
Josh Lehan31058fd2023-01-13 11:06:16 -08008#include "util.hpp"
Patrick Ventured1491722019-02-08 14:37:45 -08009
Patrick Ventured1491722019-02-08 14:37:45 -080010#include <nlohmann/json.hpp>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070011
Ed Tanousf8b6e552025-06-27 13:27:50 -070012#include <cstddef>
13#include <cstdint>
Bonnie Lo0e8fc392022-10-05 10:20:55 +080014#include <iostream>
Josh Lehan31058fd2023-01-13 11:06:16 -080015#include <limits>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070016#include <map>
Ed Tanousf8b6e552025-06-27 13:27:50 -070017#include <string>
18#include <utility>
19#include <vector>
Patrick Ventured1491722019-02-08 14:37:45 -080020
Patrick Venturea0764872020-08-08 07:48:43 -070021namespace pid_control
22{
23
Patrick Ventured1491722019-02-08 14:37:45 -080024using json = nlohmann::json;
25
James Feistf81f2882019-02-26 11:26:36 -080026namespace conf
27{
Josh Lehan31058fd2023-01-13 11:06:16 -080028
James Feistf81f2882019-02-26 11:26:36 -080029void from_json(const json& j, conf::ControllerInfo& c)
Patrick Ventured1491722019-02-08 14:37:45 -080030{
Josh Lehan31058fd2023-01-13 11:06:16 -080031 std::vector<std::string> inputNames;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -080032 std::vector<std::string> missingAcceptableNames;
Josh Lehan31058fd2023-01-13 11:06:16 -080033
Patrick Ventured1491722019-02-08 14:37:45 -080034 j.at("type").get_to(c.type);
Josh Lehan31058fd2023-01-13 11:06:16 -080035 j.at("inputs").get_to(inputNames);
Patrick Ventured1491722019-02-08 14:37:45 -080036 j.at("setpoint").get_to(c.setpoint);
37
Josh Lehan31058fd2023-01-13 11:06:16 -080038 std::vector<double> inputTempToMargin;
39
40 auto findTempToMargin = j.find("tempToMargin");
41 if (findTempToMargin != j.end())
42 {
43 findTempToMargin->get_to(inputTempToMargin);
44 }
45
Josh Lehan3f0f7bc2023-02-13 01:45:29 -080046 auto findMissingAcceptable = j.find("missingIsAcceptable");
47 if (findMissingAcceptable != j.end())
48 {
49 findMissingAcceptable->get_to(missingAcceptableNames);
50 }
51
Patrick Williamsbd63bca2024-08-16 15:21:10 -040052 c.inputs =
53 spliceInputs(inputNames, inputTempToMargin, missingAcceptableNames);
Josh Lehan31058fd2023-01-13 11:06:16 -080054
Patrick Ventured1491722019-02-08 14:37:45 -080055 /* TODO: We need to handle parsing other PID controller configurations.
56 * We can do that by checking for different keys and making the decision
57 * accordingly.
58 */
59 auto p = j.at("pid");
Patrick Ventured1491722019-02-08 14:37:45 -080060
Delphine CC Chiu5d897e22024-06-04 13:33:22 +080061 auto checkHysterWithSetpt = p.find("checkHysteresisWithSetpoint");
Patrick Ventured1491722019-02-08 14:37:45 -080062 auto positiveHysteresis = p.find("positiveHysteresis");
Hank Liou375f7092019-03-29 20:15:42 +080063 auto negativeHysteresis = p.find("negativeHysteresis");
Josh Lehanc612c052022-12-12 09:56:47 -080064 auto derivativeCoeff = p.find("derivativeCoeff");
Delphine CC Chiu97889632023-11-06 11:32:46 +080065 auto checkHysterWithSetptValue = false;
Hank Liou375f7092019-03-29 20:15:42 +080066 auto positiveHysteresisValue = 0.0;
67 auto negativeHysteresisValue = 0.0;
Josh Lehanc612c052022-12-12 09:56:47 -080068 auto derivativeCoeffValue = 0.0;
Delphine CC Chiu97889632023-11-06 11:32:46 +080069 if (checkHysterWithSetpt != p.end())
70 {
71 checkHysterWithSetpt->get_to(checkHysterWithSetptValue);
72 }
Hank Liou375f7092019-03-29 20:15:42 +080073 if (positiveHysteresis != p.end())
Patrick Ventured1491722019-02-08 14:37:45 -080074 {
Josh Lehanc612c052022-12-12 09:56:47 -080075 positiveHysteresis->get_to(positiveHysteresisValue);
Patrick Ventured1491722019-02-08 14:37:45 -080076 }
Hank Liou375f7092019-03-29 20:15:42 +080077 if (negativeHysteresis != p.end())
Patrick Ventured1491722019-02-08 14:37:45 -080078 {
Josh Lehanc612c052022-12-12 09:56:47 -080079 negativeHysteresis->get_to(negativeHysteresisValue);
80 }
81 if (derivativeCoeff != p.end())
82 {
83 derivativeCoeff->get_to(derivativeCoeffValue);
Patrick Ventured1491722019-02-08 14:37:45 -080084 }
85
ykchiu9fe3a3c2023-05-11 13:43:54 +080086 auto failSafePercent = j.find("FailSafePercent");
87 auto failSafePercentValue = 0;
88 if (failSafePercent != j.end())
89 {
90 failSafePercent->get_to(failSafePercentValue);
91 }
92 c.failSafePercent = failSafePercentValue;
93
Hank Liou375f7092019-03-29 20:15:42 +080094 if (c.type != "stepwise")
Patrick Ventured1491722019-02-08 14:37:45 -080095 {
Hank Liou375f7092019-03-29 20:15:42 +080096 p.at("samplePeriod").get_to(c.pidInfo.ts);
97 p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff);
98 p.at("integralCoeff").get_to(c.pidInfo.integralCoeff);
99 p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset);
100 p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain);
101 p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min);
102 p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max);
103 p.at("outLim_min").get_to(c.pidInfo.outLim.min);
104 p.at("outLim_max").get_to(c.pidInfo.outLim.max);
105 p.at("slewNeg").get_to(c.pidInfo.slewNeg);
106 p.at("slewPos").get_to(c.pidInfo.slewPos);
107
Josh Lehanc612c052022-12-12 09:56:47 -0800108 // Unlike other coefficients, treat derivativeCoeff as an optional
109 // parameter, as support for it is fairly new, to avoid breaking
110 // existing configurations in the field that predate it.
Hank Liou375f7092019-03-29 20:15:42 +0800111 c.pidInfo.positiveHysteresis = positiveHysteresisValue;
112 c.pidInfo.negativeHysteresis = negativeHysteresisValue;
Josh Lehanc612c052022-12-12 09:56:47 -0800113 c.pidInfo.derivativeCoeff = derivativeCoeffValue;
Delphine CC Chiu97889632023-11-06 11:32:46 +0800114 c.pidInfo.checkHysterWithSetpt = checkHysterWithSetptValue;
Patrick Ventured1491722019-02-08 14:37:45 -0800115 }
116 else
117 {
Hank Liou375f7092019-03-29 20:15:42 +0800118 p.at("samplePeriod").get_to(c.stepwiseInfo.ts);
119 p.at("isCeiling").get_to(c.stepwiseInfo.isCeiling);
120
121 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
122 {
123 c.stepwiseInfo.reading[i] =
124 std::numeric_limits<double>::quiet_NaN();
125 c.stepwiseInfo.output[i] = std::numeric_limits<double>::quiet_NaN();
126 }
127
128 auto reading = p.find("reading");
129 if (reading != p.end())
130 {
131 auto r = p.at("reading");
132 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
133 {
134 auto n = r.find(std::to_string(i));
135 if (n != r.end())
136 {
137 r.at(std::to_string(i)).get_to(c.stepwiseInfo.reading[i]);
138 }
139 }
140 }
141
142 auto output = p.find("output");
143 if (output != p.end())
144 {
145 auto o = p.at("output");
146 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
147 {
148 auto n = o.find(std::to_string(i));
149 if (n != o.end())
150 {
151 o.at(std::to_string(i)).get_to(c.stepwiseInfo.output[i]);
152 }
153 }
154 }
155
156 c.stepwiseInfo.positiveHysteresis = positiveHysteresisValue;
157 c.stepwiseInfo.negativeHysteresis = negativeHysteresisValue;
Patrick Ventured1491722019-02-08 14:37:45 -0800158 }
159}
Josh Lehan31058fd2023-01-13 11:06:16 -0800160
James Feistf81f2882019-02-26 11:26:36 -0800161} // namespace conf
Patrick Ventured1491722019-02-08 14:37:45 -0800162
Harvey Wu239aa7d2022-11-18 08:43:34 +0800163inline void getCycleTimeSetting(const auto& zone, const int id,
164 const std::string& attributeName,
165 uint64_t& value)
166{
167 auto findAttributeName = zone.find(attributeName);
168 if (findAttributeName != zone.end())
169 {
170 uint64_t tmpAttributeValue = 0;
171 findAttributeName->get_to(tmpAttributeValue);
172 if (tmpAttributeValue >= 1)
173 {
174 value = tmpAttributeValue;
175 }
176 else
177 {
178 std::cerr << "Zone " << id << ": " << attributeName
179 << " is invalid. Use default " << value << " ms\n";
180 }
181 }
182 else
183 {
184 std::cerr << "Zone " << id << ": " << attributeName
185 << " cannot find setting. Use default " << value << " ms\n";
186 }
187}
188
Patrick Venture1df9e872020-10-08 15:35:01 -0700189std::pair<std::map<int64_t, conf::PIDConf>, std::map<int64_t, conf::ZoneConfig>>
Patrick Ventured1491722019-02-08 14:37:45 -0800190 buildPIDsFromJson(const json& data)
191{
192 // zone -> pids
James Feistf81f2882019-02-26 11:26:36 -0800193 std::map<int64_t, conf::PIDConf> pidConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800194 // zone -> configs
Patrick Venture1df9e872020-10-08 15:35:01 -0700195 std::map<int64_t, conf::ZoneConfig> zoneConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800196
197 /* TODO: if zones is empty, that's invalid. */
198 auto zones = data["zones"];
199 for (const auto& zone : zones)
200 {
201 int64_t id;
James Feistf81f2882019-02-26 11:26:36 -0800202 conf::PIDConf thisZone;
Patrick Venture1df9e872020-10-08 15:35:01 -0700203 conf::ZoneConfig thisZoneConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800204
205 /* TODO: using at() throws a specific exception we can catch */
206 id = zone["id"];
James Feist3484bed2019-02-25 13:28:18 -0800207 thisZoneConfig.minThermalOutput = zone["minThermalOutput"];
Patrick Ventured1491722019-02-08 14:37:45 -0800208 thisZoneConfig.failsafePercent = zone["failsafePercent"];
209
Harvey Wu239aa7d2022-11-18 08:43:34 +0800210 getCycleTimeSetting(zone, id, "cycleIntervalTimeMS",
211 thisZoneConfig.cycleTime.cycleIntervalTimeMS);
212 getCycleTimeSetting(zone, id, "updateThermalsTimeMS",
213 thisZoneConfig.cycleTime.updateThermalsTimeMS);
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800214
Delphine CC Chiu97889632023-11-06 11:32:46 +0800215 bool accumulateSetPoint = false;
216 auto findAccSetPoint = zone.find("accumulateSetPoint");
217 if (findAccSetPoint != zone.end())
218 {
219 findAccSetPoint->get_to(accumulateSetPoint);
220 }
221 thisZoneConfig.accumulateSetPoint = accumulateSetPoint;
222
Patrick Ventured1491722019-02-08 14:37:45 -0800223 auto pids = zone["pids"];
224 for (const auto& pid : pids)
225 {
226 auto name = pid["name"];
James Feistf81f2882019-02-26 11:26:36 -0800227 auto item = pid.get<conf::ControllerInfo>();
Patrick Ventured1491722019-02-08 14:37:45 -0800228
ykchiu7c6d35d2023-05-10 17:01:46 +0800229 if (thisZone.find(name) != thisZone.end())
230 {
231 std::cerr << "Warning: zone " << id
232 << " have the same pid name " << name << std::endl;
233 }
234
Patrick Ventured1491722019-02-08 14:37:45 -0800235 thisZone[name] = item;
236 }
237
238 pidConfig[id] = thisZone;
239 zoneConfig[id] = thisZoneConfig;
240 }
241
242 return std::make_pair(pidConfig, zoneConfig);
243}
Patrick Venturea0764872020-08-08 07:48:43 -0700244
245} // namespace pid_control