blob: dd7d2a3042a8466ec4f3ad0d4ebde73b75789202 [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"
Josh Lehan31058fd2023-01-13 11:06:16 -080020#include "util.hpp"
Patrick Ventured1491722019-02-08 14:37:45 -080021
Patrick Ventured1491722019-02-08 14:37:45 -080022#include <nlohmann/json.hpp>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070023
Bonnie Lo0e8fc392022-10-05 10:20:55 +080024#include <iostream>
Josh Lehan31058fd2023-01-13 11:06:16 -080025#include <limits>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070026#include <map>
Patrick Ventured1491722019-02-08 14:37:45 -080027#include <tuple>
28
Patrick Venturea0764872020-08-08 07:48:43 -070029namespace pid_control
30{
31
Patrick Ventured1491722019-02-08 14:37:45 -080032using json = nlohmann::json;
33
James Feistf81f2882019-02-26 11:26:36 -080034namespace conf
35{
Josh Lehan31058fd2023-01-13 11:06:16 -080036
James Feistf81f2882019-02-26 11:26:36 -080037void from_json(const json& j, conf::ControllerInfo& c)
Patrick Ventured1491722019-02-08 14:37:45 -080038{
Josh Lehan31058fd2023-01-13 11:06:16 -080039 std::vector<std::string> inputNames;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -080040 std::vector<std::string> missingAcceptableNames;
Josh Lehan31058fd2023-01-13 11:06:16 -080041
Patrick Ventured1491722019-02-08 14:37:45 -080042 j.at("type").get_to(c.type);
Josh Lehan31058fd2023-01-13 11:06:16 -080043 j.at("inputs").get_to(inputNames);
Patrick Ventured1491722019-02-08 14:37:45 -080044 j.at("setpoint").get_to(c.setpoint);
45
Josh Lehan31058fd2023-01-13 11:06:16 -080046 std::vector<double> inputTempToMargin;
47
48 auto findTempToMargin = j.find("tempToMargin");
49 if (findTempToMargin != j.end())
50 {
51 findTempToMargin->get_to(inputTempToMargin);
52 }
53
Josh Lehan3f0f7bc2023-02-13 01:45:29 -080054 auto findMissingAcceptable = j.find("missingIsAcceptable");
55 if (findMissingAcceptable != j.end())
56 {
57 findMissingAcceptable->get_to(missingAcceptableNames);
58 }
59
60 c.inputs = spliceInputs(inputNames, inputTempToMargin,
61 missingAcceptableNames);
Josh Lehan31058fd2023-01-13 11:06:16 -080062
Patrick Ventured1491722019-02-08 14:37:45 -080063 /* TODO: We need to handle parsing other PID controller configurations.
64 * We can do that by checking for different keys and making the decision
65 * accordingly.
66 */
67 auto p = j.at("pid");
Patrick Ventured1491722019-02-08 14:37:45 -080068
69 auto positiveHysteresis = p.find("positiveHysteresis");
Hank Liou375f7092019-03-29 20:15:42 +080070 auto negativeHysteresis = p.find("negativeHysteresis");
Josh Lehanc612c052022-12-12 09:56:47 -080071 auto derivativeCoeff = p.find("derivativeCoeff");
Hank Liou375f7092019-03-29 20:15:42 +080072 auto positiveHysteresisValue = 0.0;
73 auto negativeHysteresisValue = 0.0;
Josh Lehanc612c052022-12-12 09:56:47 -080074 auto derivativeCoeffValue = 0.0;
Hank Liou375f7092019-03-29 20:15:42 +080075 if (positiveHysteresis != p.end())
Patrick Ventured1491722019-02-08 14:37:45 -080076 {
Josh Lehanc612c052022-12-12 09:56:47 -080077 positiveHysteresis->get_to(positiveHysteresisValue);
Patrick Ventured1491722019-02-08 14:37:45 -080078 }
Hank Liou375f7092019-03-29 20:15:42 +080079 if (negativeHysteresis != p.end())
Patrick Ventured1491722019-02-08 14:37:45 -080080 {
Josh Lehanc612c052022-12-12 09:56:47 -080081 negativeHysteresis->get_to(negativeHysteresisValue);
82 }
83 if (derivativeCoeff != p.end())
84 {
85 derivativeCoeff->get_to(derivativeCoeffValue);
Patrick Ventured1491722019-02-08 14:37:45 -080086 }
87
ykchiu9fe3a3c2023-05-11 13:43:54 +080088 auto failSafePercent = j.find("FailSafePercent");
89 auto failSafePercentValue = 0;
90 if (failSafePercent != j.end())
91 {
92 failSafePercent->get_to(failSafePercentValue);
93 }
94 c.failSafePercent = failSafePercentValue;
95
Hank Liou375f7092019-03-29 20:15:42 +080096 if (c.type != "stepwise")
Patrick Ventured1491722019-02-08 14:37:45 -080097 {
Hank Liou375f7092019-03-29 20:15:42 +080098 p.at("samplePeriod").get_to(c.pidInfo.ts);
99 p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff);
100 p.at("integralCoeff").get_to(c.pidInfo.integralCoeff);
101 p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset);
102 p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain);
103 p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min);
104 p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max);
105 p.at("outLim_min").get_to(c.pidInfo.outLim.min);
106 p.at("outLim_max").get_to(c.pidInfo.outLim.max);
107 p.at("slewNeg").get_to(c.pidInfo.slewNeg);
108 p.at("slewPos").get_to(c.pidInfo.slewPos);
109
Josh Lehanc612c052022-12-12 09:56:47 -0800110 // Unlike other coefficients, treat derivativeCoeff as an optional
111 // parameter, as support for it is fairly new, to avoid breaking
112 // existing configurations in the field that predate it.
Hank Liou375f7092019-03-29 20:15:42 +0800113 c.pidInfo.positiveHysteresis = positiveHysteresisValue;
114 c.pidInfo.negativeHysteresis = negativeHysteresisValue;
Josh Lehanc612c052022-12-12 09:56:47 -0800115 c.pidInfo.derivativeCoeff = derivativeCoeffValue;
Patrick Ventured1491722019-02-08 14:37:45 -0800116 }
117 else
118 {
Hank Liou375f7092019-03-29 20:15:42 +0800119 p.at("samplePeriod").get_to(c.stepwiseInfo.ts);
120 p.at("isCeiling").get_to(c.stepwiseInfo.isCeiling);
121
122 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
123 {
124 c.stepwiseInfo.reading[i] =
125 std::numeric_limits<double>::quiet_NaN();
126 c.stepwiseInfo.output[i] = std::numeric_limits<double>::quiet_NaN();
127 }
128
129 auto reading = p.find("reading");
130 if (reading != p.end())
131 {
132 auto r = p.at("reading");
133 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
134 {
135 auto n = r.find(std::to_string(i));
136 if (n != r.end())
137 {
138 r.at(std::to_string(i)).get_to(c.stepwiseInfo.reading[i]);
139 }
140 }
141 }
142
143 auto output = p.find("output");
144 if (output != p.end())
145 {
146 auto o = p.at("output");
147 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
148 {
149 auto n = o.find(std::to_string(i));
150 if (n != o.end())
151 {
152 o.at(std::to_string(i)).get_to(c.stepwiseInfo.output[i]);
153 }
154 }
155 }
156
157 c.stepwiseInfo.positiveHysteresis = positiveHysteresisValue;
158 c.stepwiseInfo.negativeHysteresis = negativeHysteresisValue;
Patrick Ventured1491722019-02-08 14:37:45 -0800159 }
160}
Josh Lehan31058fd2023-01-13 11:06:16 -0800161
James Feistf81f2882019-02-26 11:26:36 -0800162} // namespace conf
Patrick Ventured1491722019-02-08 14:37:45 -0800163
Harvey Wu239aa7d2022-11-18 08:43:34 +0800164inline void getCycleTimeSetting(const auto& zone, const int id,
165 const std::string& attributeName,
166 uint64_t& value)
167{
168 auto findAttributeName = zone.find(attributeName);
169 if (findAttributeName != zone.end())
170 {
171 uint64_t tmpAttributeValue = 0;
172 findAttributeName->get_to(tmpAttributeValue);
173 if (tmpAttributeValue >= 1)
174 {
175 value = tmpAttributeValue;
176 }
177 else
178 {
179 std::cerr << "Zone " << id << ": " << attributeName
180 << " is invalid. Use default " << value << " ms\n";
181 }
182 }
183 else
184 {
185 std::cerr << "Zone " << id << ": " << attributeName
186 << " cannot find setting. Use default " << value << " ms\n";
187 }
188}
189
Patrick Venture1df9e872020-10-08 15:35:01 -0700190std::pair<std::map<int64_t, conf::PIDConf>, std::map<int64_t, conf::ZoneConfig>>
Patrick Ventured1491722019-02-08 14:37:45 -0800191 buildPIDsFromJson(const json& data)
192{
193 // zone -> pids
James Feistf81f2882019-02-26 11:26:36 -0800194 std::map<int64_t, conf::PIDConf> pidConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800195 // zone -> configs
Patrick Venture1df9e872020-10-08 15:35:01 -0700196 std::map<int64_t, conf::ZoneConfig> zoneConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800197
198 /* TODO: if zones is empty, that's invalid. */
199 auto zones = data["zones"];
200 for (const auto& zone : zones)
201 {
202 int64_t id;
James Feistf81f2882019-02-26 11:26:36 -0800203 conf::PIDConf thisZone;
Patrick Venture1df9e872020-10-08 15:35:01 -0700204 conf::ZoneConfig thisZoneConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800205
206 /* TODO: using at() throws a specific exception we can catch */
207 id = zone["id"];
James Feist3484bed2019-02-25 13:28:18 -0800208 thisZoneConfig.minThermalOutput = zone["minThermalOutput"];
Patrick Ventured1491722019-02-08 14:37:45 -0800209 thisZoneConfig.failsafePercent = zone["failsafePercent"];
210
Harvey Wu239aa7d2022-11-18 08:43:34 +0800211 getCycleTimeSetting(zone, id, "cycleIntervalTimeMS",
212 thisZoneConfig.cycleTime.cycleIntervalTimeMS);
213 getCycleTimeSetting(zone, id, "updateThermalsTimeMS",
214 thisZoneConfig.cycleTime.updateThermalsTimeMS);
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800215
Patrick Ventured1491722019-02-08 14:37:45 -0800216 auto pids = zone["pids"];
217 for (const auto& pid : pids)
218 {
219 auto name = pid["name"];
James Feistf81f2882019-02-26 11:26:36 -0800220 auto item = pid.get<conf::ControllerInfo>();
Patrick Ventured1491722019-02-08 14:37:45 -0800221
ykchiu7c6d35d2023-05-10 17:01:46 +0800222 if (thisZone.find(name) != thisZone.end())
223 {
224 std::cerr << "Warning: zone " << id
225 << " have the same pid name " << name << std::endl;
226 }
227
Patrick Ventured1491722019-02-08 14:37:45 -0800228 thisZone[name] = item;
229 }
230
231 pidConfig[id] = thisZone;
232 zoneConfig[id] = thisZoneConfig;
233 }
234
235 return std::make_pair(pidConfig, zoneConfig);
236}
Patrick Venturea0764872020-08-08 07:48:43 -0700237
238} // namespace pid_control