blob: e633890d2adacba7c1acfae22cfaeb3c86a62a62 [file] [log] [blame]
Patrick Ventured1491722019-02-08 14:37:45 -08001/**
2 * Copyright 2019 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "pid/buildjson.hpp"
18
19#include "conf.hpp"
Ed Tanousf8b6e552025-06-27 13:27:50 -070020#include "ec/stepwise.hpp"
Josh Lehan31058fd2023-01-13 11:06:16 -080021#include "util.hpp"
Patrick Ventured1491722019-02-08 14:37:45 -080022
Patrick Ventured1491722019-02-08 14:37:45 -080023#include <nlohmann/json.hpp>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070024
Ed Tanousf8b6e552025-06-27 13:27:50 -070025#include <cstddef>
26#include <cstdint>
Bonnie Lo0e8fc392022-10-05 10:20:55 +080027#include <iostream>
Josh Lehan31058fd2023-01-13 11:06:16 -080028#include <limits>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070029#include <map>
Ed Tanousf8b6e552025-06-27 13:27:50 -070030#include <string>
31#include <utility>
32#include <vector>
Patrick Ventured1491722019-02-08 14:37:45 -080033
Patrick Venturea0764872020-08-08 07:48:43 -070034namespace pid_control
35{
36
Patrick Ventured1491722019-02-08 14:37:45 -080037using json = nlohmann::json;
38
James Feistf81f2882019-02-26 11:26:36 -080039namespace conf
40{
Josh Lehan31058fd2023-01-13 11:06:16 -080041
James Feistf81f2882019-02-26 11:26:36 -080042void from_json(const json& j, conf::ControllerInfo& c)
Patrick Ventured1491722019-02-08 14:37:45 -080043{
Josh Lehan31058fd2023-01-13 11:06:16 -080044 std::vector<std::string> inputNames;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -080045 std::vector<std::string> missingAcceptableNames;
Josh Lehan31058fd2023-01-13 11:06:16 -080046
Patrick Ventured1491722019-02-08 14:37:45 -080047 j.at("type").get_to(c.type);
Josh Lehan31058fd2023-01-13 11:06:16 -080048 j.at("inputs").get_to(inputNames);
Patrick Ventured1491722019-02-08 14:37:45 -080049 j.at("setpoint").get_to(c.setpoint);
50
Josh Lehan31058fd2023-01-13 11:06:16 -080051 std::vector<double> inputTempToMargin;
52
53 auto findTempToMargin = j.find("tempToMargin");
54 if (findTempToMargin != j.end())
55 {
56 findTempToMargin->get_to(inputTempToMargin);
57 }
58
Josh Lehan3f0f7bc2023-02-13 01:45:29 -080059 auto findMissingAcceptable = j.find("missingIsAcceptable");
60 if (findMissingAcceptable != j.end())
61 {
62 findMissingAcceptable->get_to(missingAcceptableNames);
63 }
64
Patrick Williamsbd63bca2024-08-16 15:21:10 -040065 c.inputs =
66 spliceInputs(inputNames, inputTempToMargin, missingAcceptableNames);
Josh Lehan31058fd2023-01-13 11:06:16 -080067
Patrick Ventured1491722019-02-08 14:37:45 -080068 /* TODO: We need to handle parsing other PID controller configurations.
69 * We can do that by checking for different keys and making the decision
70 * accordingly.
71 */
72 auto p = j.at("pid");
Patrick Ventured1491722019-02-08 14:37:45 -080073
Delphine CC Chiu5d897e22024-06-04 13:33:22 +080074 auto checkHysterWithSetpt = p.find("checkHysteresisWithSetpoint");
Patrick Ventured1491722019-02-08 14:37:45 -080075 auto positiveHysteresis = p.find("positiveHysteresis");
Hank Liou375f7092019-03-29 20:15:42 +080076 auto negativeHysteresis = p.find("negativeHysteresis");
Josh Lehanc612c052022-12-12 09:56:47 -080077 auto derivativeCoeff = p.find("derivativeCoeff");
Delphine CC Chiu97889632023-11-06 11:32:46 +080078 auto checkHysterWithSetptValue = false;
Hank Liou375f7092019-03-29 20:15:42 +080079 auto positiveHysteresisValue = 0.0;
80 auto negativeHysteresisValue = 0.0;
Josh Lehanc612c052022-12-12 09:56:47 -080081 auto derivativeCoeffValue = 0.0;
Delphine CC Chiu97889632023-11-06 11:32:46 +080082 if (checkHysterWithSetpt != p.end())
83 {
84 checkHysterWithSetpt->get_to(checkHysterWithSetptValue);
85 }
Hank Liou375f7092019-03-29 20:15:42 +080086 if (positiveHysteresis != p.end())
Patrick Ventured1491722019-02-08 14:37:45 -080087 {
Josh Lehanc612c052022-12-12 09:56:47 -080088 positiveHysteresis->get_to(positiveHysteresisValue);
Patrick Ventured1491722019-02-08 14:37:45 -080089 }
Hank Liou375f7092019-03-29 20:15:42 +080090 if (negativeHysteresis != p.end())
Patrick Ventured1491722019-02-08 14:37:45 -080091 {
Josh Lehanc612c052022-12-12 09:56:47 -080092 negativeHysteresis->get_to(negativeHysteresisValue);
93 }
94 if (derivativeCoeff != p.end())
95 {
96 derivativeCoeff->get_to(derivativeCoeffValue);
Patrick Ventured1491722019-02-08 14:37:45 -080097 }
98
ykchiu9fe3a3c2023-05-11 13:43:54 +080099 auto failSafePercent = j.find("FailSafePercent");
100 auto failSafePercentValue = 0;
101 if (failSafePercent != j.end())
102 {
103 failSafePercent->get_to(failSafePercentValue);
104 }
105 c.failSafePercent = failSafePercentValue;
106
Hank Liou375f7092019-03-29 20:15:42 +0800107 if (c.type != "stepwise")
Patrick Ventured1491722019-02-08 14:37:45 -0800108 {
Hank Liou375f7092019-03-29 20:15:42 +0800109 p.at("samplePeriod").get_to(c.pidInfo.ts);
110 p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff);
111 p.at("integralCoeff").get_to(c.pidInfo.integralCoeff);
112 p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset);
113 p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain);
114 p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min);
115 p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max);
116 p.at("outLim_min").get_to(c.pidInfo.outLim.min);
117 p.at("outLim_max").get_to(c.pidInfo.outLim.max);
118 p.at("slewNeg").get_to(c.pidInfo.slewNeg);
119 p.at("slewPos").get_to(c.pidInfo.slewPos);
120
Josh Lehanc612c052022-12-12 09:56:47 -0800121 // Unlike other coefficients, treat derivativeCoeff as an optional
122 // parameter, as support for it is fairly new, to avoid breaking
123 // existing configurations in the field that predate it.
Hank Liou375f7092019-03-29 20:15:42 +0800124 c.pidInfo.positiveHysteresis = positiveHysteresisValue;
125 c.pidInfo.negativeHysteresis = negativeHysteresisValue;
Josh Lehanc612c052022-12-12 09:56:47 -0800126 c.pidInfo.derivativeCoeff = derivativeCoeffValue;
Delphine CC Chiu97889632023-11-06 11:32:46 +0800127 c.pidInfo.checkHysterWithSetpt = checkHysterWithSetptValue;
Patrick Ventured1491722019-02-08 14:37:45 -0800128 }
129 else
130 {
Hank Liou375f7092019-03-29 20:15:42 +0800131 p.at("samplePeriod").get_to(c.stepwiseInfo.ts);
132 p.at("isCeiling").get_to(c.stepwiseInfo.isCeiling);
133
134 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
135 {
136 c.stepwiseInfo.reading[i] =
137 std::numeric_limits<double>::quiet_NaN();
138 c.stepwiseInfo.output[i] = std::numeric_limits<double>::quiet_NaN();
139 }
140
141 auto reading = p.find("reading");
142 if (reading != p.end())
143 {
144 auto r = p.at("reading");
145 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
146 {
147 auto n = r.find(std::to_string(i));
148 if (n != r.end())
149 {
150 r.at(std::to_string(i)).get_to(c.stepwiseInfo.reading[i]);
151 }
152 }
153 }
154
155 auto output = p.find("output");
156 if (output != p.end())
157 {
158 auto o = p.at("output");
159 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
160 {
161 auto n = o.find(std::to_string(i));
162 if (n != o.end())
163 {
164 o.at(std::to_string(i)).get_to(c.stepwiseInfo.output[i]);
165 }
166 }
167 }
168
169 c.stepwiseInfo.positiveHysteresis = positiveHysteresisValue;
170 c.stepwiseInfo.negativeHysteresis = negativeHysteresisValue;
Patrick Ventured1491722019-02-08 14:37:45 -0800171 }
172}
Josh Lehan31058fd2023-01-13 11:06:16 -0800173
James Feistf81f2882019-02-26 11:26:36 -0800174} // namespace conf
Patrick Ventured1491722019-02-08 14:37:45 -0800175
Harvey Wu239aa7d2022-11-18 08:43:34 +0800176inline void getCycleTimeSetting(const auto& zone, const int id,
177 const std::string& attributeName,
178 uint64_t& value)
179{
180 auto findAttributeName = zone.find(attributeName);
181 if (findAttributeName != zone.end())
182 {
183 uint64_t tmpAttributeValue = 0;
184 findAttributeName->get_to(tmpAttributeValue);
185 if (tmpAttributeValue >= 1)
186 {
187 value = tmpAttributeValue;
188 }
189 else
190 {
191 std::cerr << "Zone " << id << ": " << attributeName
192 << " is invalid. Use default " << value << " ms\n";
193 }
194 }
195 else
196 {
197 std::cerr << "Zone " << id << ": " << attributeName
198 << " cannot find setting. Use default " << value << " ms\n";
199 }
200}
201
Patrick Venture1df9e872020-10-08 15:35:01 -0700202std::pair<std::map<int64_t, conf::PIDConf>, std::map<int64_t, conf::ZoneConfig>>
Patrick Ventured1491722019-02-08 14:37:45 -0800203 buildPIDsFromJson(const json& data)
204{
205 // zone -> pids
James Feistf81f2882019-02-26 11:26:36 -0800206 std::map<int64_t, conf::PIDConf> pidConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800207 // zone -> configs
Patrick Venture1df9e872020-10-08 15:35:01 -0700208 std::map<int64_t, conf::ZoneConfig> zoneConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800209
210 /* TODO: if zones is empty, that's invalid. */
211 auto zones = data["zones"];
212 for (const auto& zone : zones)
213 {
214 int64_t id;
James Feistf81f2882019-02-26 11:26:36 -0800215 conf::PIDConf thisZone;
Patrick Venture1df9e872020-10-08 15:35:01 -0700216 conf::ZoneConfig thisZoneConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800217
218 /* TODO: using at() throws a specific exception we can catch */
219 id = zone["id"];
James Feist3484bed2019-02-25 13:28:18 -0800220 thisZoneConfig.minThermalOutput = zone["minThermalOutput"];
Patrick Ventured1491722019-02-08 14:37:45 -0800221 thisZoneConfig.failsafePercent = zone["failsafePercent"];
222
Harvey Wu239aa7d2022-11-18 08:43:34 +0800223 getCycleTimeSetting(zone, id, "cycleIntervalTimeMS",
224 thisZoneConfig.cycleTime.cycleIntervalTimeMS);
225 getCycleTimeSetting(zone, id, "updateThermalsTimeMS",
226 thisZoneConfig.cycleTime.updateThermalsTimeMS);
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800227
Delphine CC Chiu97889632023-11-06 11:32:46 +0800228 bool accumulateSetPoint = false;
229 auto findAccSetPoint = zone.find("accumulateSetPoint");
230 if (findAccSetPoint != zone.end())
231 {
232 findAccSetPoint->get_to(accumulateSetPoint);
233 }
234 thisZoneConfig.accumulateSetPoint = accumulateSetPoint;
235
Patrick Ventured1491722019-02-08 14:37:45 -0800236 auto pids = zone["pids"];
237 for (const auto& pid : pids)
238 {
239 auto name = pid["name"];
James Feistf81f2882019-02-26 11:26:36 -0800240 auto item = pid.get<conf::ControllerInfo>();
Patrick Ventured1491722019-02-08 14:37:45 -0800241
ykchiu7c6d35d2023-05-10 17:01:46 +0800242 if (thisZone.find(name) != thisZone.end())
243 {
244 std::cerr << "Warning: zone " << id
245 << " have the same pid name " << name << std::endl;
246 }
247
Patrick Ventured1491722019-02-08 14:37:45 -0800248 thisZone[name] = item;
249 }
250
251 pidConfig[id] = thisZone;
252 zoneConfig[id] = thisZoneConfig;
253 }
254
255 return std::make_pair(pidConfig, zoneConfig);
256}
Patrick Venturea0764872020-08-08 07:48:43 -0700257
258} // namespace pid_control