1d1491724SPatrick Venture /**
2d1491724SPatrick Venture  * Copyright 2019 Google Inc.
3d1491724SPatrick Venture  *
4d1491724SPatrick Venture  * Licensed under the Apache License, Version 2.0 (the "License");
5d1491724SPatrick Venture  * you may not use this file except in compliance with the License.
6d1491724SPatrick Venture  * You may obtain a copy of the License at
7d1491724SPatrick Venture  *
8d1491724SPatrick Venture  *     http://www.apache.org/licenses/LICENSE-2.0
9d1491724SPatrick Venture  *
10d1491724SPatrick Venture  * Unless required by applicable law or agreed to in writing, software
11d1491724SPatrick Venture  * distributed under the License is distributed on an "AS IS" BASIS,
12d1491724SPatrick Venture  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d1491724SPatrick Venture  * See the License for the specific language governing permissions and
14d1491724SPatrick Venture  * limitations under the License.
15d1491724SPatrick Venture  */
16d1491724SPatrick Venture 
17d1491724SPatrick Venture #include "pid/buildjson.hpp"
18d1491724SPatrick Venture 
19d1491724SPatrick Venture #include "conf.hpp"
2031058fd3SJosh Lehan #include "util.hpp"
21d1491724SPatrick Venture 
22d1491724SPatrick Venture #include <nlohmann/json.hpp>
23a83a3eccSPatrick Venture 
240e8fc398SBonnie Lo #include <iostream>
2531058fd3SJosh Lehan #include <limits>
26a83a3eccSPatrick Venture #include <map>
27d1491724SPatrick Venture #include <tuple>
28d1491724SPatrick Venture 
29a076487aSPatrick Venture namespace pid_control
30a076487aSPatrick Venture {
31a076487aSPatrick Venture 
32d1491724SPatrick Venture using json = nlohmann::json;
33d1491724SPatrick Venture 
34f81f2886SJames Feist namespace conf
35f81f2886SJames Feist {
3631058fd3SJosh Lehan 
37f81f2886SJames Feist void from_json(const json& j, conf::ControllerInfo& c)
38d1491724SPatrick Venture {
3931058fd3SJosh Lehan     std::vector<std::string> inputNames;
40*3f0f7bc3SJosh Lehan     std::vector<std::string> missingAcceptableNames;
4131058fd3SJosh Lehan 
42d1491724SPatrick Venture     j.at("type").get_to(c.type);
4331058fd3SJosh Lehan     j.at("inputs").get_to(inputNames);
44d1491724SPatrick Venture     j.at("setpoint").get_to(c.setpoint);
45d1491724SPatrick Venture 
4631058fd3SJosh Lehan     std::vector<double> inputTempToMargin;
4731058fd3SJosh Lehan 
4831058fd3SJosh Lehan     auto findTempToMargin = j.find("tempToMargin");
4931058fd3SJosh Lehan     if (findTempToMargin != j.end())
5031058fd3SJosh Lehan     {
5131058fd3SJosh Lehan         findTempToMargin->get_to(inputTempToMargin);
5231058fd3SJosh Lehan     }
5331058fd3SJosh Lehan 
54*3f0f7bc3SJosh Lehan     auto findMissingAcceptable = j.find("missingIsAcceptable");
55*3f0f7bc3SJosh Lehan     if (findMissingAcceptable != j.end())
56*3f0f7bc3SJosh Lehan     {
57*3f0f7bc3SJosh Lehan         findMissingAcceptable->get_to(missingAcceptableNames);
58*3f0f7bc3SJosh Lehan     }
59*3f0f7bc3SJosh Lehan 
60*3f0f7bc3SJosh Lehan     c.inputs = spliceInputs(inputNames, inputTempToMargin,
61*3f0f7bc3SJosh Lehan                             missingAcceptableNames);
6231058fd3SJosh Lehan 
63d1491724SPatrick Venture     /* TODO: We need to handle parsing other PID controller configurations.
64d1491724SPatrick Venture      * We can do that by checking for different keys and making the decision
65d1491724SPatrick Venture      * accordingly.
66d1491724SPatrick Venture      */
67d1491724SPatrick Venture     auto p = j.at("pid");
68375f7098SHank Liou 
69375f7098SHank Liou     auto positiveHysteresis = p.find("positiveHysteresis");
70375f7098SHank Liou     auto negativeHysteresis = p.find("negativeHysteresis");
71c612c051SJosh Lehan     auto derivativeCoeff = p.find("derivativeCoeff");
72375f7098SHank Liou     auto positiveHysteresisValue = 0.0;
73375f7098SHank Liou     auto negativeHysteresisValue = 0.0;
74c612c051SJosh Lehan     auto derivativeCoeffValue = 0.0;
75375f7098SHank Liou     if (positiveHysteresis != p.end())
76375f7098SHank Liou     {
77c612c051SJosh Lehan         positiveHysteresis->get_to(positiveHysteresisValue);
78375f7098SHank Liou     }
79375f7098SHank Liou     if (negativeHysteresis != p.end())
80375f7098SHank Liou     {
81c612c051SJosh Lehan         negativeHysteresis->get_to(negativeHysteresisValue);
82c612c051SJosh Lehan     }
83c612c051SJosh Lehan     if (derivativeCoeff != p.end())
84c612c051SJosh Lehan     {
85c612c051SJosh Lehan         derivativeCoeff->get_to(derivativeCoeffValue);
86375f7098SHank Liou     }
87375f7098SHank Liou 
889fe3a3c7Sykchiu     auto failSafePercent = j.find("FailSafePercent");
899fe3a3c7Sykchiu     auto failSafePercentValue = 0;
909fe3a3c7Sykchiu     if (failSafePercent != j.end())
919fe3a3c7Sykchiu     {
929fe3a3c7Sykchiu         failSafePercent->get_to(failSafePercentValue);
939fe3a3c7Sykchiu     }
949fe3a3c7Sykchiu     c.failSafePercent = failSafePercentValue;
959fe3a3c7Sykchiu 
96375f7098SHank Liou     if (c.type != "stepwise")
97375f7098SHank Liou     {
98d1491724SPatrick Venture         p.at("samplePeriod").get_to(c.pidInfo.ts);
99d1491724SPatrick Venture         p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff);
100d1491724SPatrick Venture         p.at("integralCoeff").get_to(c.pidInfo.integralCoeff);
101903b0427SPatrick Venture         p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset);
102d1491724SPatrick Venture         p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain);
103d1491724SPatrick Venture         p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min);
104d1491724SPatrick Venture         p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max);
105d1491724SPatrick Venture         p.at("outLim_min").get_to(c.pidInfo.outLim.min);
106d1491724SPatrick Venture         p.at("outLim_max").get_to(c.pidInfo.outLim.max);
107d1491724SPatrick Venture         p.at("slewNeg").get_to(c.pidInfo.slewNeg);
108d1491724SPatrick Venture         p.at("slewPos").get_to(c.pidInfo.slewPos);
109d1491724SPatrick Venture 
110c612c051SJosh Lehan         // Unlike other coefficients, treat derivativeCoeff as an optional
111c612c051SJosh Lehan         // parameter, as support for it is fairly new, to avoid breaking
112c612c051SJosh Lehan         // existing configurations in the field that predate it.
113375f7098SHank Liou         c.pidInfo.positiveHysteresis = positiveHysteresisValue;
114375f7098SHank Liou         c.pidInfo.negativeHysteresis = negativeHysteresisValue;
115c612c051SJosh Lehan         c.pidInfo.derivativeCoeff = derivativeCoeffValue;
116d1491724SPatrick Venture     }
117d1491724SPatrick Venture     else
118d1491724SPatrick Venture     {
119375f7098SHank Liou         p.at("samplePeriod").get_to(c.stepwiseInfo.ts);
120375f7098SHank Liou         p.at("isCeiling").get_to(c.stepwiseInfo.isCeiling);
121375f7098SHank Liou 
122375f7098SHank Liou         for (size_t i = 0; i < ec::maxStepwisePoints; i++)
123375f7098SHank Liou         {
124375f7098SHank Liou             c.stepwiseInfo.reading[i] =
125375f7098SHank Liou                 std::numeric_limits<double>::quiet_NaN();
126375f7098SHank Liou             c.stepwiseInfo.output[i] = std::numeric_limits<double>::quiet_NaN();
127d1491724SPatrick Venture         }
128d1491724SPatrick Venture 
129375f7098SHank Liou         auto reading = p.find("reading");
130375f7098SHank Liou         if (reading != p.end())
131d1491724SPatrick Venture         {
132375f7098SHank Liou             auto r = p.at("reading");
133375f7098SHank Liou             for (size_t i = 0; i < ec::maxStepwisePoints; i++)
134375f7098SHank Liou             {
135375f7098SHank Liou                 auto n = r.find(std::to_string(i));
136375f7098SHank Liou                 if (n != r.end())
137375f7098SHank Liou                 {
138375f7098SHank Liou                     r.at(std::to_string(i)).get_to(c.stepwiseInfo.reading[i]);
139d1491724SPatrick Venture                 }
140375f7098SHank Liou             }
141375f7098SHank Liou         }
142375f7098SHank Liou 
143375f7098SHank Liou         auto output = p.find("output");
144375f7098SHank Liou         if (output != p.end())
145d1491724SPatrick Venture         {
146375f7098SHank Liou             auto o = p.at("output");
147375f7098SHank Liou             for (size_t i = 0; i < ec::maxStepwisePoints; i++)
148375f7098SHank Liou             {
149375f7098SHank Liou                 auto n = o.find(std::to_string(i));
150375f7098SHank Liou                 if (n != o.end())
151375f7098SHank Liou                 {
152375f7098SHank Liou                     o.at(std::to_string(i)).get_to(c.stepwiseInfo.output[i]);
153375f7098SHank Liou                 }
154375f7098SHank Liou             }
155375f7098SHank Liou         }
156375f7098SHank Liou 
157375f7098SHank Liou         c.stepwiseInfo.positiveHysteresis = positiveHysteresisValue;
158375f7098SHank Liou         c.stepwiseInfo.negativeHysteresis = negativeHysteresisValue;
159d1491724SPatrick Venture     }
160d1491724SPatrick Venture }
16131058fd3SJosh Lehan 
162f81f2886SJames Feist } // namespace conf
163d1491724SPatrick Venture 
164239aa7d7SHarvey Wu inline void getCycleTimeSetting(const auto& zone, const int id,
165239aa7d7SHarvey Wu                                 const std::string& attributeName,
166239aa7d7SHarvey Wu                                 uint64_t& value)
167239aa7d7SHarvey Wu {
168239aa7d7SHarvey Wu     auto findAttributeName = zone.find(attributeName);
169239aa7d7SHarvey Wu     if (findAttributeName != zone.end())
170239aa7d7SHarvey Wu     {
171239aa7d7SHarvey Wu         uint64_t tmpAttributeValue = 0;
172239aa7d7SHarvey Wu         findAttributeName->get_to(tmpAttributeValue);
173239aa7d7SHarvey Wu         if (tmpAttributeValue >= 1)
174239aa7d7SHarvey Wu         {
175239aa7d7SHarvey Wu             value = tmpAttributeValue;
176239aa7d7SHarvey Wu         }
177239aa7d7SHarvey Wu         else
178239aa7d7SHarvey Wu         {
179239aa7d7SHarvey Wu             std::cerr << "Zone " << id << ": " << attributeName
180239aa7d7SHarvey Wu                       << " is invalid. Use default " << value << " ms\n";
181239aa7d7SHarvey Wu         }
182239aa7d7SHarvey Wu     }
183239aa7d7SHarvey Wu     else
184239aa7d7SHarvey Wu     {
185239aa7d7SHarvey Wu         std::cerr << "Zone " << id << ": " << attributeName
186239aa7d7SHarvey Wu                   << " cannot find setting. Use default " << value << " ms\n";
187239aa7d7SHarvey Wu     }
188239aa7d7SHarvey Wu }
189239aa7d7SHarvey Wu 
1901df9e879SPatrick Venture std::pair<std::map<int64_t, conf::PIDConf>, std::map<int64_t, conf::ZoneConfig>>
191d1491724SPatrick Venture     buildPIDsFromJson(const json& data)
192d1491724SPatrick Venture {
193d1491724SPatrick Venture     // zone -> pids
194f81f2886SJames Feist     std::map<int64_t, conf::PIDConf> pidConfig;
195d1491724SPatrick Venture     // zone -> configs
1961df9e879SPatrick Venture     std::map<int64_t, conf::ZoneConfig> zoneConfig;
197d1491724SPatrick Venture 
198d1491724SPatrick Venture     /* TODO: if zones is empty, that's invalid. */
199d1491724SPatrick Venture     auto zones = data["zones"];
200d1491724SPatrick Venture     for (const auto& zone : zones)
201d1491724SPatrick Venture     {
202d1491724SPatrick Venture         int64_t id;
203f81f2886SJames Feist         conf::PIDConf thisZone;
2041df9e879SPatrick Venture         conf::ZoneConfig thisZoneConfig;
205d1491724SPatrick Venture 
206d1491724SPatrick Venture         /* TODO: using at() throws a specific exception we can catch */
207d1491724SPatrick Venture         id = zone["id"];
2083484bedaSJames Feist         thisZoneConfig.minThermalOutput = zone["minThermalOutput"];
209d1491724SPatrick Venture         thisZoneConfig.failsafePercent = zone["failsafePercent"];
210d1491724SPatrick Venture 
211239aa7d7SHarvey Wu         getCycleTimeSetting(zone, id, "cycleIntervalTimeMS",
212239aa7d7SHarvey Wu                             thisZoneConfig.cycleTime.cycleIntervalTimeMS);
213239aa7d7SHarvey Wu         getCycleTimeSetting(zone, id, "updateThermalsTimeMS",
214239aa7d7SHarvey Wu                             thisZoneConfig.cycleTime.updateThermalsTimeMS);
2150e8fc398SBonnie Lo 
216d1491724SPatrick Venture         auto pids = zone["pids"];
217d1491724SPatrick Venture         for (const auto& pid : pids)
218d1491724SPatrick Venture         {
219d1491724SPatrick Venture             auto name = pid["name"];
220f81f2886SJames Feist             auto item = pid.get<conf::ControllerInfo>();
221d1491724SPatrick Venture 
2227c6d35d5Sykchiu             if (thisZone.find(name) != thisZone.end())
2237c6d35d5Sykchiu             {
2247c6d35d5Sykchiu                 std::cerr << "Warning: zone " << id
2257c6d35d5Sykchiu                           << " have the same pid name " << name << std::endl;
2267c6d35d5Sykchiu             }
2277c6d35d5Sykchiu 
228d1491724SPatrick Venture             thisZone[name] = item;
229d1491724SPatrick Venture         }
230d1491724SPatrick Venture 
231d1491724SPatrick Venture         pidConfig[id] = thisZone;
232d1491724SPatrick Venture         zoneConfig[id] = thisZoneConfig;
233d1491724SPatrick Venture     }
234d1491724SPatrick Venture 
235d1491724SPatrick Venture     return std::make_pair(pidConfig, zoneConfig);
236d1491724SPatrick Venture }
237a076487aSPatrick Venture 
238a076487aSPatrick Venture } // namespace pid_control
239