1 /** 2 * Copyright © 2020 IBM Corporation 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 #include "zone.hpp" 17 18 #include "../zone.hpp" 19 #include "functor.hpp" 20 #include "handlers.hpp" 21 #include "types.hpp" 22 23 #include <nlohmann/json.hpp> 24 #include <phosphor-logging/log.hpp> 25 #include <sdbusplus/bus.hpp> 26 27 #include <iterator> 28 #include <map> 29 #include <numeric> 30 #include <utility> 31 #include <vector> 32 33 namespace phosphor::fan::control::json 34 { 35 36 using json = nlohmann::json; 37 using namespace phosphor::logging; 38 39 const std::map<std::string, std::map<std::string, propHandler>> 40 Zone::_intfPropHandlers = {{thermModeIntf, 41 {{supportedProp, zone::property::supported}, 42 {currentProp, zone::property::current}}}}; 43 44 Zone::Zone(sdbusplus::bus::bus& bus, const json& jsonObj) : 45 ConfigBase(jsonObj), _incDelay(0) 46 { 47 if (jsonObj.contains("profiles")) 48 { 49 for (const auto& profile : jsonObj["profiles"]) 50 { 51 _profiles.emplace_back(profile.get<std::string>()); 52 } 53 } 54 // Speed increase delay is optional, defaults to 0 55 if (jsonObj.contains("increase_delay")) 56 { 57 _incDelay = jsonObj["increase_delay"].get<uint64_t>(); 58 } 59 setFullSpeed(jsonObj); 60 setDefaultFloor(jsonObj); 61 setDecInterval(jsonObj); 62 // Setting properties on interfaces to be served are optional 63 if (jsonObj.contains("interfaces")) 64 { 65 setInterfaces(jsonObj); 66 } 67 } 68 69 void Zone::setFullSpeed(const json& jsonObj) 70 { 71 if (!jsonObj.contains("full_speed")) 72 { 73 log<level::ERR>("Missing required zone's full speed", 74 entry("JSON=%s", jsonObj.dump().c_str())); 75 throw std::runtime_error("Missing required zone's full speed"); 76 } 77 _fullSpeed = jsonObj["full_speed"].get<uint64_t>(); 78 } 79 80 void Zone::setDefaultFloor(const json& jsonObj) 81 { 82 if (!jsonObj.contains("default_floor")) 83 { 84 log<level::ERR>("Missing required zone's default floor speed", 85 entry("JSON=%s", jsonObj.dump().c_str())); 86 throw std::runtime_error("Missing required zone's default floor speed"); 87 } 88 _defaultFloor = jsonObj["default_floor"].get<uint64_t>(); 89 } 90 91 void Zone::setDecInterval(const json& jsonObj) 92 { 93 if (!jsonObj.contains("decrease_interval")) 94 { 95 log<level::ERR>("Missing required zone's decrease interval", 96 entry("JSON=%s", jsonObj.dump().c_str())); 97 throw std::runtime_error("Missing required zone's decrease interval"); 98 } 99 _decInterval = jsonObj["decrease_interval"].get<uint64_t>(); 100 } 101 102 void Zone::setInterfaces(const json& jsonObj) 103 { 104 for (const auto& interface : jsonObj["interfaces"]) 105 { 106 if (!interface.contains("name") || !interface.contains("properties")) 107 { 108 log<level::ERR>("Missing required zone interface attributes", 109 entry("JSON=%s", interface.dump().c_str())); 110 throw std::runtime_error( 111 "Missing required zone interface attributes"); 112 } 113 auto propFuncs = 114 _intfPropHandlers.find(interface["name"].get<std::string>()); 115 if (propFuncs == _intfPropHandlers.end()) 116 { 117 // Construct list of available configurable interfaces 118 auto intfs = std::accumulate( 119 std::next(_intfPropHandlers.begin()), _intfPropHandlers.end(), 120 _intfPropHandlers.begin()->first, [](auto list, auto intf) { 121 return std::move(list) + ", " + intf.first; 122 }); 123 log<level::ERR>("Configured interface not available", 124 entry("JSON=%s", interface.dump().c_str()), 125 entry("AVAILABLE_INTFS=%s", intfs.c_str())); 126 throw std::runtime_error("Configured interface not available"); 127 } 128 129 for (const auto& property : interface["properties"]) 130 { 131 if (!property.contains("name")) 132 { 133 log<level::ERR>( 134 "Missing required interface property attributes", 135 entry("JSON=%s", property.dump().c_str())); 136 throw std::runtime_error( 137 "Missing required interface property attributes"); 138 } 139 // Attribute "persist" is optional, defaults to `false` 140 auto persist = false; 141 if (property.contains("persist")) 142 { 143 persist = property["persist"].get<bool>(); 144 } 145 // Property name from JSON must exactly match supported 146 // index names to functions in property namespace 147 auto propFunc = 148 propFuncs->second.find(property["name"].get<std::string>()); 149 if (propFunc == propFuncs->second.end()) 150 { 151 // Construct list of available configurable properties 152 auto props = std::accumulate( 153 std::next(propFuncs->second.begin()), 154 propFuncs->second.end(), propFuncs->second.begin()->first, 155 [](auto list, auto prop) { 156 return std::move(list) + ", " + prop.first; 157 }); 158 log<level::ERR>("Configured property not available", 159 entry("JSON=%s", property.dump().c_str()), 160 entry("AVAILABLE_PROPS=%s", props.c_str())); 161 throw std::runtime_error( 162 "Configured property function not available"); 163 } 164 auto zHandler = propFunc->second(property, persist); 165 // Only add non-null zone handler functions 166 if (zHandler) 167 { 168 _zoneHandlers.emplace_back(zHandler); 169 } 170 } 171 } 172 } 173 174 /** 175 * Properties of interfaces supported by the zone configuration that return 176 * a ZoneHandler function that sets the zone's property value(s). 177 */ 178 namespace zone::property 179 { 180 // Get a zone handler function for the configured values of the "Supported" 181 // property 182 ZoneHandler supported(const json& jsonObj, bool persist) 183 { 184 std::vector<std::string> values; 185 if (!jsonObj.contains("values")) 186 { 187 log<level::ERR>( 188 "No 'values' found for \"Supported\" property, using an empty list", 189 entry("JSON=%s", jsonObj.dump().c_str())); 190 } 191 else 192 { 193 for (const auto& value : jsonObj["values"]) 194 { 195 if (!value.contains("value")) 196 { 197 log<level::ERR>("No 'value' found for \"Supported\" property " 198 "entry, skipping", 199 entry("JSON=%s", value.dump().c_str())); 200 } 201 else 202 { 203 values.emplace_back(value["value"].get<std::string>()); 204 } 205 } 206 } 207 208 return make_zoneHandler(handler::setZoneProperty<std::vector<std::string>>( 209 Zone::thermModeIntf, Zone::supportedProp, &control::Zone::supported, 210 std::move(values), persist)); 211 } 212 213 // Get a zone handler function for a configured value of the "Current" 214 // property 215 ZoneHandler current(const json& jsonObj, bool persist) 216 { 217 // Use default value for "Current" property if no "value" entry given 218 if (!jsonObj.contains("value")) 219 { 220 log<level::ERR>("No 'value' found for \"Current\" property, " 221 "using default", 222 entry("JSON=%s", jsonObj.dump().c_str())); 223 return {}; 224 } 225 226 return make_zoneHandler(handler::setZoneProperty<std::string>( 227 Zone::thermModeIntf, Zone::currentProp, &control::Zone::current, 228 jsonObj["value"].get<std::string>(), persist)); 229 } 230 } // namespace zone::property 231 232 } // namespace phosphor::fan::control::json 233