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 <iostream> 24 #include <map> 25 #include <tuple> 26 27 namespace pid_control 28 { 29 30 using json = nlohmann::json; 31 32 namespace conf 33 { 34 void from_json(const json& j, conf::ControllerInfo& c) 35 { 36 j.at("type").get_to(c.type); 37 j.at("inputs").get_to(c.inputs); 38 j.at("setpoint").get_to(c.setpoint); 39 40 /* TODO: We need to handle parsing other PID controller configurations. 41 * We can do that by checking for different keys and making the decision 42 * accordingly. 43 */ 44 auto p = j.at("pid"); 45 46 auto positiveHysteresis = p.find("positiveHysteresis"); 47 auto negativeHysteresis = p.find("negativeHysteresis"); 48 auto positiveHysteresisValue = 0.0; 49 auto negativeHysteresisValue = 0.0; 50 if (positiveHysteresis != p.end()) 51 { 52 p.at("positiveHysteresis").get_to(positiveHysteresisValue); 53 } 54 if (negativeHysteresis != p.end()) 55 { 56 p.at("negativeHysteresis").get_to(negativeHysteresisValue); 57 } 58 59 if (c.type != "stepwise") 60 { 61 p.at("samplePeriod").get_to(c.pidInfo.ts); 62 p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff); 63 p.at("integralCoeff").get_to(c.pidInfo.integralCoeff); 64 p.at("derivativeCoeff").get_to(c.pidInfo.derivativeCoeff); 65 p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset); 66 p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain); 67 p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min); 68 p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max); 69 p.at("outLim_min").get_to(c.pidInfo.outLim.min); 70 p.at("outLim_max").get_to(c.pidInfo.outLim.max); 71 p.at("slewNeg").get_to(c.pidInfo.slewNeg); 72 p.at("slewPos").get_to(c.pidInfo.slewPos); 73 74 c.pidInfo.positiveHysteresis = positiveHysteresisValue; 75 c.pidInfo.negativeHysteresis = negativeHysteresisValue; 76 } 77 else 78 { 79 p.at("samplePeriod").get_to(c.stepwiseInfo.ts); 80 p.at("isCeiling").get_to(c.stepwiseInfo.isCeiling); 81 82 for (size_t i = 0; i < ec::maxStepwisePoints; i++) 83 { 84 c.stepwiseInfo.reading[i] = 85 std::numeric_limits<double>::quiet_NaN(); 86 c.stepwiseInfo.output[i] = std::numeric_limits<double>::quiet_NaN(); 87 } 88 89 auto reading = p.find("reading"); 90 if (reading != p.end()) 91 { 92 auto r = p.at("reading"); 93 for (size_t i = 0; i < ec::maxStepwisePoints; i++) 94 { 95 auto n = r.find(std::to_string(i)); 96 if (n != r.end()) 97 { 98 r.at(std::to_string(i)).get_to(c.stepwiseInfo.reading[i]); 99 } 100 } 101 } 102 103 auto output = p.find("output"); 104 if (output != p.end()) 105 { 106 auto o = p.at("output"); 107 for (size_t i = 0; i < ec::maxStepwisePoints; i++) 108 { 109 auto n = o.find(std::to_string(i)); 110 if (n != o.end()) 111 { 112 o.at(std::to_string(i)).get_to(c.stepwiseInfo.output[i]); 113 } 114 } 115 } 116 117 c.stepwiseInfo.positiveHysteresis = positiveHysteresisValue; 118 c.stepwiseInfo.negativeHysteresis = negativeHysteresisValue; 119 } 120 } 121 } // namespace conf 122 123 std::pair<std::map<int64_t, conf::PIDConf>, std::map<int64_t, conf::ZoneConfig>> 124 buildPIDsFromJson(const json& data) 125 { 126 // zone -> pids 127 std::map<int64_t, conf::PIDConf> pidConfig; 128 // zone -> configs 129 std::map<int64_t, conf::ZoneConfig> zoneConfig; 130 131 /* TODO: if zones is empty, that's invalid. */ 132 auto zones = data["zones"]; 133 for (const auto& zone : zones) 134 { 135 int64_t id; 136 conf::PIDConf thisZone; 137 conf::ZoneConfig thisZoneConfig; 138 139 /* TODO: using at() throws a specific exception we can catch */ 140 id = zone["id"]; 141 thisZoneConfig.minThermalOutput = zone["minThermalOutput"]; 142 thisZoneConfig.failsafePercent = zone["failsafePercent"]; 143 144 auto findTimeInterval = zone.find("cycleIntervalTimeMS"); 145 if (findTimeInterval != zone.end()) 146 { 147 uint64_t tmp; 148 findTimeInterval->get_to(tmp); 149 if (tmp != 0) 150 { 151 thisZoneConfig.cycleTime.cycleIntervalTimeMS = tmp; 152 } 153 else 154 { 155 std::cerr << "cycleIntervalTimeMS cannot be 0. Use default " 156 << thisZoneConfig.cycleTime.cycleIntervalTimeMS 157 << " ms\n"; 158 } 159 } 160 161 auto findUpdateThermalsTime = zone.find("updateThermalsTimeMS"); 162 if (findUpdateThermalsTime != zone.end()) 163 { 164 uint64_t tmp; 165 findUpdateThermalsTime->get_to(tmp); 166 if (tmp != 0) 167 { 168 thisZoneConfig.cycleTime.updateThermalsTimeMS = tmp; 169 } 170 else 171 { 172 std::cerr << "updateThermalsTimeMS cannot be 0. Use default " 173 << thisZoneConfig.cycleTime.updateThermalsTimeMS 174 << " ms\n"; 175 } 176 } 177 178 double updateCount = 179 double(thisZoneConfig.cycleTime.updateThermalsTimeMS) / 180 double(thisZoneConfig.cycleTime.cycleIntervalTimeMS); 181 182 /* Check if updateThermalsTimeMS could be divided by cycleIntervalTimeMS 183 * without leaving a remainder */ 184 if (updateCount != std::ceil(updateCount)) 185 { 186 std::cerr 187 << "updateThermalsTimeMS cannot be divided by " 188 "cycleIntervalTimeMS without leaving a remainder. Using the " 189 "smallest integer that is not less than the result.\n"; 190 updateCount = std::ceil(updateCount); 191 } 192 thisZoneConfig.cycleTime.updateThermalsTimeMS = updateCount; 193 194 auto pids = zone["pids"]; 195 for (const auto& pid : pids) 196 { 197 auto name = pid["name"]; 198 auto item = pid.get<conf::ControllerInfo>(); 199 200 thisZone[name] = item; 201 } 202 203 pidConfig[id] = thisZone; 204 zoneConfig[id] = thisZoneConfig; 205 } 206 207 return std::make_pair(pidConfig, zoneConfig); 208 } 209 210 } // namespace pid_control 211