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 <nlohmann/json.hpp> 22 23 #include <map> 24 #include <tuple> 25 26 namespace pid_control 27 { 28 29 using json = nlohmann::json; 30 31 namespace conf 32 { 33 void from_json(const json& j, conf::ControllerInfo& c) 34 { 35 j.at("type").get_to(c.type); 36 j.at("inputs").get_to(c.inputs); 37 j.at("setpoint").get_to(c.setpoint); 38 39 /* TODO: We need to handle parsing other PID controller configurations. 40 * We can do that by checking for different keys and making the decision 41 * accordingly. 42 */ 43 auto p = j.at("pid"); 44 45 auto positiveHysteresis = p.find("positiveHysteresis"); 46 auto negativeHysteresis = p.find("negativeHysteresis"); 47 auto positiveHysteresisValue = 0.0; 48 auto negativeHysteresisValue = 0.0; 49 if (positiveHysteresis != p.end()) 50 { 51 p.at("positiveHysteresis").get_to(positiveHysteresisValue); 52 } 53 if (negativeHysteresis != p.end()) 54 { 55 p.at("negativeHysteresis").get_to(negativeHysteresisValue); 56 } 57 58 if (c.type != "stepwise") 59 { 60 p.at("samplePeriod").get_to(c.pidInfo.ts); 61 p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff); 62 p.at("integralCoeff").get_to(c.pidInfo.integralCoeff); 63 p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset); 64 p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain); 65 p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min); 66 p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max); 67 p.at("outLim_min").get_to(c.pidInfo.outLim.min); 68 p.at("outLim_max").get_to(c.pidInfo.outLim.max); 69 p.at("slewNeg").get_to(c.pidInfo.slewNeg); 70 p.at("slewPos").get_to(c.pidInfo.slewPos); 71 72 c.pidInfo.positiveHysteresis = positiveHysteresisValue; 73 c.pidInfo.negativeHysteresis = negativeHysteresisValue; 74 } 75 else 76 { 77 p.at("samplePeriod").get_to(c.stepwiseInfo.ts); 78 p.at("isCeiling").get_to(c.stepwiseInfo.isCeiling); 79 80 for (size_t i = 0; i < ec::maxStepwisePoints; i++) 81 { 82 c.stepwiseInfo.reading[i] = 83 std::numeric_limits<double>::quiet_NaN(); 84 c.stepwiseInfo.output[i] = std::numeric_limits<double>::quiet_NaN(); 85 } 86 87 auto reading = p.find("reading"); 88 if (reading != p.end()) 89 { 90 auto r = p.at("reading"); 91 for (size_t i = 0; i < ec::maxStepwisePoints; i++) 92 { 93 auto n = r.find(std::to_string(i)); 94 if (n != r.end()) 95 { 96 r.at(std::to_string(i)).get_to(c.stepwiseInfo.reading[i]); 97 } 98 } 99 } 100 101 auto output = p.find("output"); 102 if (output != p.end()) 103 { 104 auto o = p.at("output"); 105 for (size_t i = 0; i < ec::maxStepwisePoints; i++) 106 { 107 auto n = o.find(std::to_string(i)); 108 if (n != o.end()) 109 { 110 o.at(std::to_string(i)).get_to(c.stepwiseInfo.output[i]); 111 } 112 } 113 } 114 115 c.stepwiseInfo.positiveHysteresis = positiveHysteresisValue; 116 c.stepwiseInfo.negativeHysteresis = negativeHysteresisValue; 117 } 118 } 119 } // namespace conf 120 121 std::pair<std::map<int64_t, conf::PIDConf>, 122 std::map<int64_t, struct conf::ZoneConfig>> 123 buildPIDsFromJson(const json& data) 124 { 125 // zone -> pids 126 std::map<int64_t, conf::PIDConf> pidConfig; 127 // zone -> configs 128 std::map<int64_t, struct conf::ZoneConfig> zoneConfig; 129 130 /* TODO: if zones is empty, that's invalid. */ 131 auto zones = data["zones"]; 132 for (const auto& zone : zones) 133 { 134 int64_t id; 135 conf::PIDConf thisZone; 136 struct conf::ZoneConfig thisZoneConfig; 137 138 /* TODO: using at() throws a specific exception we can catch */ 139 id = zone["id"]; 140 thisZoneConfig.minThermalOutput = zone["minThermalOutput"]; 141 thisZoneConfig.failsafePercent = zone["failsafePercent"]; 142 143 auto pids = zone["pids"]; 144 for (const auto& pid : pids) 145 { 146 auto name = pid["name"]; 147 auto item = pid.get<conf::ControllerInfo>(); 148 149 thisZone[name] = item; 150 } 151 152 pidConfig[id] = thisZone; 153 zoneConfig[id] = thisZoneConfig; 154 } 155 156 return std::make_pair(pidConfig, zoneConfig); 157 } 158 159 } // namespace pid_control 160