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