1 /**
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 
21 #include <map>
22 #include <nlohmann/json.hpp>
23 #include <tuple>
24 
25 using json = nlohmann::json;
26 
27 namespace conf
28 {
29 void from_json(const json& j, conf::ControllerInfo& c)
30 {
31     j.at("type").get_to(c.type);
32     j.at("inputs").get_to(c.inputs);
33     j.at("setpoint").get_to(c.setpoint);
34 
35     /* TODO: We need to handle parsing other PID controller configurations.
36      * We can do that by checking for different keys and making the decision
37      * accordingly.
38      */
39     auto p = j.at("pid");
40     p.at("samplePeriod").get_to(c.pidInfo.ts);
41     p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff);
42     p.at("integralCoeff").get_to(c.pidInfo.integralCoeff);
43     p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset);
44     p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain);
45     p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min);
46     p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max);
47     p.at("outLim_min").get_to(c.pidInfo.outLim.min);
48     p.at("outLim_max").get_to(c.pidInfo.outLim.max);
49     p.at("slewNeg").get_to(c.pidInfo.slewNeg);
50     p.at("slewPos").get_to(c.pidInfo.slewPos);
51 
52     auto positiveHysteresis = p.find("positiveHysteresis");
53     if (positiveHysteresis == p.end())
54     {
55         c.pidInfo.positiveHysteresis = 0.0;
56     }
57     else
58     {
59         j.at("positiveHysteresis").get_to(c.pidInfo.positiveHysteresis);
60     }
61 
62     auto negativeHysteresis = p.find("negativeHysteresis");
63     if (negativeHysteresis == p.end())
64     {
65         c.pidInfo.negativeHysteresis = 0.0;
66     }
67     else
68     {
69         j.at("negativeHysteresis").get_to(c.pidInfo.negativeHysteresis);
70     }
71 }
72 } // namespace conf
73 
74 std::pair<std::map<int64_t, conf::PIDConf>,
75           std::map<int64_t, struct conf::ZoneConfig>>
76     buildPIDsFromJson(const json& data)
77 {
78     // zone -> pids
79     std::map<int64_t, conf::PIDConf> pidConfig;
80     // zone -> configs
81     std::map<int64_t, struct conf::ZoneConfig> zoneConfig;
82 
83     /* TODO: if zones is empty, that's invalid. */
84     auto zones = data["zones"];
85     for (const auto& zone : zones)
86     {
87         int64_t id;
88         conf::PIDConf thisZone;
89         struct conf::ZoneConfig thisZoneConfig;
90 
91         /* TODO: using at() throws a specific exception we can catch */
92         id = zone["id"];
93         thisZoneConfig.minThermalOutput = zone["minThermalOutput"];
94         thisZoneConfig.failsafePercent = zone["failsafePercent"];
95 
96         auto pids = zone["pids"];
97         for (const auto& pid : pids)
98         {
99             auto name = pid["name"];
100             auto item = pid.get<conf::ControllerInfo>();
101 
102             thisZone[name] = item;
103         }
104 
105         pidConfig[id] = thisZone;
106         zoneConfig[id] = thisZoneConfig;
107     }
108 
109     return std::make_pair(pidConfig, zoneConfig);
110 }
111