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