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 
from_json(const json & j,conf::ControllerInfo & c)37f81f2886SJames Feist void from_json(const json& j, conf::ControllerInfo& c)
38d1491724SPatrick Venture {
3931058fd3SJosh Lehan     std::vector<std::string> inputNames;
403f0f7bc3SJosh 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 
543f0f7bc3SJosh Lehan     auto findMissingAcceptable = j.find("missingIsAcceptable");
553f0f7bc3SJosh Lehan     if (findMissingAcceptable != j.end())
563f0f7bc3SJosh Lehan     {
573f0f7bc3SJosh Lehan         findMissingAcceptable->get_to(missingAcceptableNames);
583f0f7bc3SJosh Lehan     }
593f0f7bc3SJosh Lehan 
603f0f7bc3SJosh Lehan     c.inputs = spliceInputs(inputNames, inputTempToMargin,
613f0f7bc3SJosh 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 
69*5d897e2aSDelphine CC Chiu     auto checkHysterWithSetpt = p.find("checkHysteresisWithSetpoint");
70375f7098SHank Liou     auto positiveHysteresis = p.find("positiveHysteresis");
71375f7098SHank Liou     auto negativeHysteresis = p.find("negativeHysteresis");
72c612c051SJosh Lehan     auto derivativeCoeff = p.find("derivativeCoeff");
739788963cSDelphine CC Chiu     auto checkHysterWithSetptValue = false;
74375f7098SHank Liou     auto positiveHysteresisValue = 0.0;
75375f7098SHank Liou     auto negativeHysteresisValue = 0.0;
76c612c051SJosh Lehan     auto derivativeCoeffValue = 0.0;
779788963cSDelphine CC Chiu     if (checkHysterWithSetpt != p.end())
789788963cSDelphine CC Chiu     {
799788963cSDelphine CC Chiu         checkHysterWithSetpt->get_to(checkHysterWithSetptValue);
809788963cSDelphine CC Chiu     }
81375f7098SHank Liou     if (positiveHysteresis != p.end())
82375f7098SHank Liou     {
83c612c051SJosh Lehan         positiveHysteresis->get_to(positiveHysteresisValue);
84375f7098SHank Liou     }
85375f7098SHank Liou     if (negativeHysteresis != p.end())
86375f7098SHank Liou     {
87c612c051SJosh Lehan         negativeHysteresis->get_to(negativeHysteresisValue);
88c612c051SJosh Lehan     }
89c612c051SJosh Lehan     if (derivativeCoeff != p.end())
90c612c051SJosh Lehan     {
91c612c051SJosh Lehan         derivativeCoeff->get_to(derivativeCoeffValue);
92375f7098SHank Liou     }
93375f7098SHank Liou 
949fe3a3c7Sykchiu     auto failSafePercent = j.find("FailSafePercent");
959fe3a3c7Sykchiu     auto failSafePercentValue = 0;
969fe3a3c7Sykchiu     if (failSafePercent != j.end())
979fe3a3c7Sykchiu     {
989fe3a3c7Sykchiu         failSafePercent->get_to(failSafePercentValue);
999fe3a3c7Sykchiu     }
1009fe3a3c7Sykchiu     c.failSafePercent = failSafePercentValue;
1019fe3a3c7Sykchiu 
102375f7098SHank Liou     if (c.type != "stepwise")
103375f7098SHank Liou     {
104d1491724SPatrick Venture         p.at("samplePeriod").get_to(c.pidInfo.ts);
105d1491724SPatrick Venture         p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff);
106d1491724SPatrick Venture         p.at("integralCoeff").get_to(c.pidInfo.integralCoeff);
107903b0427SPatrick Venture         p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset);
108d1491724SPatrick Venture         p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain);
109d1491724SPatrick Venture         p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min);
110d1491724SPatrick Venture         p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max);
111d1491724SPatrick Venture         p.at("outLim_min").get_to(c.pidInfo.outLim.min);
112d1491724SPatrick Venture         p.at("outLim_max").get_to(c.pidInfo.outLim.max);
113d1491724SPatrick Venture         p.at("slewNeg").get_to(c.pidInfo.slewNeg);
114d1491724SPatrick Venture         p.at("slewPos").get_to(c.pidInfo.slewPos);
115d1491724SPatrick Venture 
116c612c051SJosh Lehan         // Unlike other coefficients, treat derivativeCoeff as an optional
117c612c051SJosh Lehan         // parameter, as support for it is fairly new, to avoid breaking
118c612c051SJosh Lehan         // existing configurations in the field that predate it.
119375f7098SHank Liou         c.pidInfo.positiveHysteresis = positiveHysteresisValue;
120375f7098SHank Liou         c.pidInfo.negativeHysteresis = negativeHysteresisValue;
121c612c051SJosh Lehan         c.pidInfo.derivativeCoeff = derivativeCoeffValue;
1229788963cSDelphine CC Chiu         c.pidInfo.checkHysterWithSetpt = checkHysterWithSetptValue;
123d1491724SPatrick Venture     }
124d1491724SPatrick Venture     else
125d1491724SPatrick Venture     {
126375f7098SHank Liou         p.at("samplePeriod").get_to(c.stepwiseInfo.ts);
127375f7098SHank Liou         p.at("isCeiling").get_to(c.stepwiseInfo.isCeiling);
128375f7098SHank Liou 
129375f7098SHank Liou         for (size_t i = 0; i < ec::maxStepwisePoints; i++)
130375f7098SHank Liou         {
131375f7098SHank Liou             c.stepwiseInfo.reading[i] =
132375f7098SHank Liou                 std::numeric_limits<double>::quiet_NaN();
133375f7098SHank Liou             c.stepwiseInfo.output[i] = std::numeric_limits<double>::quiet_NaN();
134d1491724SPatrick Venture         }
135d1491724SPatrick Venture 
136375f7098SHank Liou         auto reading = p.find("reading");
137375f7098SHank Liou         if (reading != p.end())
138d1491724SPatrick Venture         {
139375f7098SHank Liou             auto r = p.at("reading");
140375f7098SHank Liou             for (size_t i = 0; i < ec::maxStepwisePoints; i++)
141375f7098SHank Liou             {
142375f7098SHank Liou                 auto n = r.find(std::to_string(i));
143375f7098SHank Liou                 if (n != r.end())
144375f7098SHank Liou                 {
145375f7098SHank Liou                     r.at(std::to_string(i)).get_to(c.stepwiseInfo.reading[i]);
146d1491724SPatrick Venture                 }
147375f7098SHank Liou             }
148375f7098SHank Liou         }
149375f7098SHank Liou 
150375f7098SHank Liou         auto output = p.find("output");
151375f7098SHank Liou         if (output != p.end())
152d1491724SPatrick Venture         {
153375f7098SHank Liou             auto o = p.at("output");
154375f7098SHank Liou             for (size_t i = 0; i < ec::maxStepwisePoints; i++)
155375f7098SHank Liou             {
156375f7098SHank Liou                 auto n = o.find(std::to_string(i));
157375f7098SHank Liou                 if (n != o.end())
158375f7098SHank Liou                 {
159375f7098SHank Liou                     o.at(std::to_string(i)).get_to(c.stepwiseInfo.output[i]);
160375f7098SHank Liou                 }
161375f7098SHank Liou             }
162375f7098SHank Liou         }
163375f7098SHank Liou 
164375f7098SHank Liou         c.stepwiseInfo.positiveHysteresis = positiveHysteresisValue;
165375f7098SHank Liou         c.stepwiseInfo.negativeHysteresis = negativeHysteresisValue;
166d1491724SPatrick Venture     }
167d1491724SPatrick Venture }
16831058fd3SJosh Lehan 
169f81f2886SJames Feist } // namespace conf
170d1491724SPatrick Venture 
getCycleTimeSetting(const auto & zone,const int id,const std::string & attributeName,uint64_t & value)171239aa7d7SHarvey Wu inline void getCycleTimeSetting(const auto& zone, const int id,
172239aa7d7SHarvey Wu                                 const std::string& attributeName,
173239aa7d7SHarvey Wu                                 uint64_t& value)
174239aa7d7SHarvey Wu {
175239aa7d7SHarvey Wu     auto findAttributeName = zone.find(attributeName);
176239aa7d7SHarvey Wu     if (findAttributeName != zone.end())
177239aa7d7SHarvey Wu     {
178239aa7d7SHarvey Wu         uint64_t tmpAttributeValue = 0;
179239aa7d7SHarvey Wu         findAttributeName->get_to(tmpAttributeValue);
180239aa7d7SHarvey Wu         if (tmpAttributeValue >= 1)
181239aa7d7SHarvey Wu         {
182239aa7d7SHarvey Wu             value = tmpAttributeValue;
183239aa7d7SHarvey Wu         }
184239aa7d7SHarvey Wu         else
185239aa7d7SHarvey Wu         {
186239aa7d7SHarvey Wu             std::cerr << "Zone " << id << ": " << attributeName
187239aa7d7SHarvey Wu                       << " is invalid. Use default " << value << " ms\n";
188239aa7d7SHarvey Wu         }
189239aa7d7SHarvey Wu     }
190239aa7d7SHarvey Wu     else
191239aa7d7SHarvey Wu     {
192239aa7d7SHarvey Wu         std::cerr << "Zone " << id << ": " << attributeName
193239aa7d7SHarvey Wu                   << " cannot find setting. Use default " << value << " ms\n";
194239aa7d7SHarvey Wu     }
195239aa7d7SHarvey Wu }
196239aa7d7SHarvey Wu 
1971df9e879SPatrick Venture std::pair<std::map<int64_t, conf::PIDConf>, std::map<int64_t, conf::ZoneConfig>>
buildPIDsFromJson(const json & data)198d1491724SPatrick Venture     buildPIDsFromJson(const json& data)
199d1491724SPatrick Venture {
200d1491724SPatrick Venture     // zone -> pids
201f81f2886SJames Feist     std::map<int64_t, conf::PIDConf> pidConfig;
202d1491724SPatrick Venture     // zone -> configs
2031df9e879SPatrick Venture     std::map<int64_t, conf::ZoneConfig> zoneConfig;
204d1491724SPatrick Venture 
205d1491724SPatrick Venture     /* TODO: if zones is empty, that's invalid. */
206d1491724SPatrick Venture     auto zones = data["zones"];
207d1491724SPatrick Venture     for (const auto& zone : zones)
208d1491724SPatrick Venture     {
209d1491724SPatrick Venture         int64_t id;
210f81f2886SJames Feist         conf::PIDConf thisZone;
2111df9e879SPatrick Venture         conf::ZoneConfig thisZoneConfig;
212d1491724SPatrick Venture 
213d1491724SPatrick Venture         /* TODO: using at() throws a specific exception we can catch */
214d1491724SPatrick Venture         id = zone["id"];
2153484bedaSJames Feist         thisZoneConfig.minThermalOutput = zone["minThermalOutput"];
216d1491724SPatrick Venture         thisZoneConfig.failsafePercent = zone["failsafePercent"];
217d1491724SPatrick Venture 
218239aa7d7SHarvey Wu         getCycleTimeSetting(zone, id, "cycleIntervalTimeMS",
219239aa7d7SHarvey Wu                             thisZoneConfig.cycleTime.cycleIntervalTimeMS);
220239aa7d7SHarvey Wu         getCycleTimeSetting(zone, id, "updateThermalsTimeMS",
221239aa7d7SHarvey Wu                             thisZoneConfig.cycleTime.updateThermalsTimeMS);
2220e8fc398SBonnie Lo 
2239788963cSDelphine CC Chiu         bool accumulateSetPoint = false;
2249788963cSDelphine CC Chiu         auto findAccSetPoint = zone.find("accumulateSetPoint");
2259788963cSDelphine CC Chiu         if (findAccSetPoint != zone.end())
2269788963cSDelphine CC Chiu         {
2279788963cSDelphine CC Chiu             findAccSetPoint->get_to(accumulateSetPoint);
2289788963cSDelphine CC Chiu         }
2299788963cSDelphine CC Chiu         thisZoneConfig.accumulateSetPoint = accumulateSetPoint;
2309788963cSDelphine CC Chiu 
231d1491724SPatrick Venture         auto pids = zone["pids"];
232d1491724SPatrick Venture         for (const auto& pid : pids)
233d1491724SPatrick Venture         {
234d1491724SPatrick Venture             auto name = pid["name"];
235f81f2886SJames Feist             auto item = pid.get<conf::ControllerInfo>();
236d1491724SPatrick Venture 
2377c6d35d5Sykchiu             if (thisZone.find(name) != thisZone.end())
2387c6d35d5Sykchiu             {
2397c6d35d5Sykchiu                 std::cerr << "Warning: zone " << id
2407c6d35d5Sykchiu                           << " have the same pid name " << name << std::endl;
2417c6d35d5Sykchiu             }
2427c6d35d5Sykchiu 
243d1491724SPatrick Venture             thisZone[name] = item;
244d1491724SPatrick Venture         }
245d1491724SPatrick Venture 
246d1491724SPatrick Venture         pidConfig[id] = thisZone;
247d1491724SPatrick Venture         zoneConfig[id] = thisZoneConfig;
248d1491724SPatrick Venture     }
249d1491724SPatrick Venture 
250d1491724SPatrick Venture     return std::make_pair(pidConfig, zoneConfig);
251d1491724SPatrick Venture }
252a076487aSPatrick Venture 
253a076487aSPatrick Venture } // namespace pid_control
254