blob: 57ea94349161bb95ccac93ed76873d11fa5cabdd [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"
20
Patrick Ventured1491722019-02-08 14:37:45 -080021#include <nlohmann/json.hpp>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070022
Bonnie Lo0e8fc392022-10-05 10:20:55 +080023#include <iostream>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070024#include <map>
Patrick Ventured1491722019-02-08 14:37:45 -080025#include <tuple>
26
Patrick Venturea0764872020-08-08 07:48:43 -070027namespace pid_control
28{
29
Patrick Ventured1491722019-02-08 14:37:45 -080030using json = nlohmann::json;
31
James Feistf81f2882019-02-26 11:26:36 -080032namespace conf
33{
34void from_json(const json& j, conf::ControllerInfo& c)
Patrick Ventured1491722019-02-08 14:37:45 -080035{
36 j.at("type").get_to(c.type);
37 j.at("inputs").get_to(c.inputs);
38 j.at("setpoint").get_to(c.setpoint);
39
40 /* TODO: We need to handle parsing other PID controller configurations.
41 * We can do that by checking for different keys and making the decision
42 * accordingly.
43 */
44 auto p = j.at("pid");
Patrick Ventured1491722019-02-08 14:37:45 -080045
46 auto positiveHysteresis = p.find("positiveHysteresis");
Hank Liou375f7092019-03-29 20:15:42 +080047 auto negativeHysteresis = p.find("negativeHysteresis");
Josh Lehanc612c052022-12-12 09:56:47 -080048 auto derivativeCoeff = p.find("derivativeCoeff");
Hank Liou375f7092019-03-29 20:15:42 +080049 auto positiveHysteresisValue = 0.0;
50 auto negativeHysteresisValue = 0.0;
Josh Lehanc612c052022-12-12 09:56:47 -080051 auto derivativeCoeffValue = 0.0;
Hank Liou375f7092019-03-29 20:15:42 +080052 if (positiveHysteresis != p.end())
Patrick Ventured1491722019-02-08 14:37:45 -080053 {
Josh Lehanc612c052022-12-12 09:56:47 -080054 positiveHysteresis->get_to(positiveHysteresisValue);
Patrick Ventured1491722019-02-08 14:37:45 -080055 }
Hank Liou375f7092019-03-29 20:15:42 +080056 if (negativeHysteresis != p.end())
Patrick Ventured1491722019-02-08 14:37:45 -080057 {
Josh Lehanc612c052022-12-12 09:56:47 -080058 negativeHysteresis->get_to(negativeHysteresisValue);
59 }
60 if (derivativeCoeff != p.end())
61 {
62 derivativeCoeff->get_to(derivativeCoeffValue);
Patrick Ventured1491722019-02-08 14:37:45 -080063 }
64
ykchiu9fe3a3c2023-05-11 13:43:54 +080065 auto failSafePercent = j.find("FailSafePercent");
66 auto failSafePercentValue = 0;
67 if (failSafePercent != j.end())
68 {
69 failSafePercent->get_to(failSafePercentValue);
70 }
71 c.failSafePercent = failSafePercentValue;
72
Hank Liou375f7092019-03-29 20:15:42 +080073 if (c.type != "stepwise")
Patrick Ventured1491722019-02-08 14:37:45 -080074 {
Hank Liou375f7092019-03-29 20:15:42 +080075 p.at("samplePeriod").get_to(c.pidInfo.ts);
76 p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff);
77 p.at("integralCoeff").get_to(c.pidInfo.integralCoeff);
78 p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset);
79 p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain);
80 p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min);
81 p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max);
82 p.at("outLim_min").get_to(c.pidInfo.outLim.min);
83 p.at("outLim_max").get_to(c.pidInfo.outLim.max);
84 p.at("slewNeg").get_to(c.pidInfo.slewNeg);
85 p.at("slewPos").get_to(c.pidInfo.slewPos);
86
Josh Lehanc612c052022-12-12 09:56:47 -080087 // Unlike other coefficients, treat derivativeCoeff as an optional
88 // parameter, as support for it is fairly new, to avoid breaking
89 // existing configurations in the field that predate it.
Hank Liou375f7092019-03-29 20:15:42 +080090 c.pidInfo.positiveHysteresis = positiveHysteresisValue;
91 c.pidInfo.negativeHysteresis = negativeHysteresisValue;
Josh Lehanc612c052022-12-12 09:56:47 -080092 c.pidInfo.derivativeCoeff = derivativeCoeffValue;
Patrick Ventured1491722019-02-08 14:37:45 -080093 }
94 else
95 {
Hank Liou375f7092019-03-29 20:15:42 +080096 p.at("samplePeriod").get_to(c.stepwiseInfo.ts);
97 p.at("isCeiling").get_to(c.stepwiseInfo.isCeiling);
98
99 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
100 {
101 c.stepwiseInfo.reading[i] =
102 std::numeric_limits<double>::quiet_NaN();
103 c.stepwiseInfo.output[i] = std::numeric_limits<double>::quiet_NaN();
104 }
105
106 auto reading = p.find("reading");
107 if (reading != p.end())
108 {
109 auto r = p.at("reading");
110 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
111 {
112 auto n = r.find(std::to_string(i));
113 if (n != r.end())
114 {
115 r.at(std::to_string(i)).get_to(c.stepwiseInfo.reading[i]);
116 }
117 }
118 }
119
120 auto output = p.find("output");
121 if (output != p.end())
122 {
123 auto o = p.at("output");
124 for (size_t i = 0; i < ec::maxStepwisePoints; i++)
125 {
126 auto n = o.find(std::to_string(i));
127 if (n != o.end())
128 {
129 o.at(std::to_string(i)).get_to(c.stepwiseInfo.output[i]);
130 }
131 }
132 }
133
134 c.stepwiseInfo.positiveHysteresis = positiveHysteresisValue;
135 c.stepwiseInfo.negativeHysteresis = negativeHysteresisValue;
Patrick Ventured1491722019-02-08 14:37:45 -0800136 }
137}
James Feistf81f2882019-02-26 11:26:36 -0800138} // namespace conf
Patrick Ventured1491722019-02-08 14:37:45 -0800139
Harvey Wu239aa7d2022-11-18 08:43:34 +0800140inline void getCycleTimeSetting(const auto& zone, const int id,
141 const std::string& attributeName,
142 uint64_t& value)
143{
144 auto findAttributeName = zone.find(attributeName);
145 if (findAttributeName != zone.end())
146 {
147 uint64_t tmpAttributeValue = 0;
148 findAttributeName->get_to(tmpAttributeValue);
149 if (tmpAttributeValue >= 1)
150 {
151 value = tmpAttributeValue;
152 }
153 else
154 {
155 std::cerr << "Zone " << id << ": " << attributeName
156 << " is invalid. Use default " << value << " ms\n";
157 }
158 }
159 else
160 {
161 std::cerr << "Zone " << id << ": " << attributeName
162 << " cannot find setting. Use default " << value << " ms\n";
163 }
164}
165
Patrick Venture1df9e872020-10-08 15:35:01 -0700166std::pair<std::map<int64_t, conf::PIDConf>, std::map<int64_t, conf::ZoneConfig>>
Patrick Ventured1491722019-02-08 14:37:45 -0800167 buildPIDsFromJson(const json& data)
168{
169 // zone -> pids
James Feistf81f2882019-02-26 11:26:36 -0800170 std::map<int64_t, conf::PIDConf> pidConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800171 // zone -> configs
Patrick Venture1df9e872020-10-08 15:35:01 -0700172 std::map<int64_t, conf::ZoneConfig> zoneConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800173
174 /* TODO: if zones is empty, that's invalid. */
175 auto zones = data["zones"];
176 for (const auto& zone : zones)
177 {
178 int64_t id;
James Feistf81f2882019-02-26 11:26:36 -0800179 conf::PIDConf thisZone;
Patrick Venture1df9e872020-10-08 15:35:01 -0700180 conf::ZoneConfig thisZoneConfig;
Patrick Ventured1491722019-02-08 14:37:45 -0800181
182 /* TODO: using at() throws a specific exception we can catch */
183 id = zone["id"];
James Feist3484bed2019-02-25 13:28:18 -0800184 thisZoneConfig.minThermalOutput = zone["minThermalOutput"];
Patrick Ventured1491722019-02-08 14:37:45 -0800185 thisZoneConfig.failsafePercent = zone["failsafePercent"];
186
Harvey Wu239aa7d2022-11-18 08:43:34 +0800187 getCycleTimeSetting(zone, id, "cycleIntervalTimeMS",
188 thisZoneConfig.cycleTime.cycleIntervalTimeMS);
189 getCycleTimeSetting(zone, id, "updateThermalsTimeMS",
190 thisZoneConfig.cycleTime.updateThermalsTimeMS);
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800191
Patrick Ventured1491722019-02-08 14:37:45 -0800192 auto pids = zone["pids"];
193 for (const auto& pid : pids)
194 {
195 auto name = pid["name"];
James Feistf81f2882019-02-26 11:26:36 -0800196 auto item = pid.get<conf::ControllerInfo>();
Patrick Ventured1491722019-02-08 14:37:45 -0800197
ykchiu7c6d35d2023-05-10 17:01:46 +0800198 if (thisZone.find(name) != thisZone.end())
199 {
200 std::cerr << "Warning: zone " << id
201 << " have the same pid name " << name << std::endl;
202 }
203
Patrick Ventured1491722019-02-08 14:37:45 -0800204 thisZone[name] = item;
205 }
206
207 pidConfig[id] = thisZone;
208 zoneConfig[id] = thisZoneConfig;
209 }
210
211 return std::make_pair(pidConfig, zoneConfig);
212}
Patrick Venturea0764872020-08-08 07:48:43 -0700213
214} // namespace pid_control