1 #include "base_device.hpp" 2 3 #include <phosphor-logging/lg2.hpp> 4 #include <xyz/openbmc_project/State/Leak/Detector/aserver.hpp> 5 6 #include <numeric> 7 8 namespace phosphor::modbus::rtu::device 9 { 10 11 PHOSPHOR_LOG2_USING; 12 13 BaseDevice::BaseDevice(sdbusplus::async::context& ctx, 14 const config::Config& config, PortIntf& serialPort, 15 EventIntf::Events& events) : 16 ctx(ctx), config(config), serialPort(serialPort), events(events) 17 { 18 createSensors(); 19 20 info("Successfully created device {NAME}", "NAME", config.name); 21 } 22 23 static auto getObjectPath(const std::string& sensorType, 24 const std::string& sensorName) 25 -> sdbusplus::message::object_path 26 { 27 return sdbusplus::message::object_path( 28 std::string(SensorValueIntf::namespace_path::value) + "/" + sensorType + 29 "/" + sensorName); 30 } 31 32 auto BaseDevice::createSensors() -> void 33 { 34 for (const auto& sensorRegister : config.sensorRegisters) 35 { 36 SensorValueIntf::properties_t initProperties = { 37 std::numeric_limits<double>::quiet_NaN(), 38 std::numeric_limits<double>::quiet_NaN(), 39 std::numeric_limits<double>::quiet_NaN(), sensorRegister.unit}; 40 41 auto sensorPath = getObjectPath( 42 sensorRegister.pathSuffix, config.name + "_" + sensorRegister.name); 43 44 auto sensor = std::make_unique<SensorValueIntf>( 45 ctx, sensorPath.str.c_str(), initProperties); 46 47 sensor->emit_added(); 48 49 sensors.emplace(sensorRegister.name, std::move(sensor)); 50 } 51 52 return; 53 } 54 55 static auto getRawIntegerFromRegister(const std::vector<uint16_t>& reg, 56 bool sign) -> int64_t 57 { 58 if (reg.empty()) 59 { 60 return 0; 61 } 62 63 uint64_t accumulator = 0; 64 for (auto val : reg) 65 { 66 accumulator = (accumulator << 16) | val; 67 } 68 69 int64_t result = 0; 70 71 if (sign) 72 { 73 if (reg.size() == 1) 74 { 75 result = static_cast<int16_t>(accumulator); 76 } 77 else if (reg.size() == 2) 78 { 79 result = static_cast<int32_t>(accumulator); 80 } 81 else 82 { 83 result = static_cast<int64_t>(accumulator); 84 } 85 } 86 else 87 { 88 if (reg.size() == 1) 89 { 90 result = static_cast<uint16_t>(accumulator); 91 } 92 else if (reg.size() == 2) 93 { 94 result = static_cast<uint32_t>(accumulator); 95 } 96 else 97 { 98 result = static_cast<int64_t>(accumulator); 99 } 100 } 101 102 return result; 103 } 104 105 auto BaseDevice::readSensorRegisters() -> sdbusplus::async::task<void> 106 { 107 while (!ctx.stop_requested()) 108 { 109 for (const auto& sensorRegister : config.sensorRegisters) 110 { 111 auto sensor = sensors.find(sensorRegister.name); 112 if (sensor == sensors.end()) 113 { 114 error("Sensor not found for {NAME}", "NAME", 115 sensorRegister.name); 116 continue; 117 } 118 119 if (sensorRegister.size > 4) 120 { 121 error("Unsupported size for register {NAME}", "NAME", 122 sensorRegister.name); 123 continue; 124 } 125 126 auto registers = std::vector<uint16_t>(sensorRegister.size); 127 auto ret = co_await serialPort.readHoldingRegisters( 128 config.address, sensorRegister.offset, config.baudRate, 129 config.parity, registers); 130 if (!ret) 131 { 132 error( 133 "Failed to read holding registers {NAME} for {DEVICE_ADDRESS}", 134 "NAME", sensorRegister.name, "DEVICE_ADDRESS", 135 config.address); 136 continue; 137 } 138 139 double regVal = static_cast<double>( 140 getRawIntegerFromRegister(registers, sensorRegister.isSigned)); 141 if (sensorRegister.format == config::SensorFormat::floatingPoint) 142 { 143 regVal = sensorRegister.shift + 144 (sensorRegister.scale * 145 (regVal / (1ULL << sensorRegister.precision))); 146 } 147 148 sensor->second->value(regVal); 149 } 150 151 co_await readStatusRegisters(); 152 153 constexpr auto pollInterval = 3; 154 co_await sdbusplus::async::sleep_for( 155 ctx, std::chrono::seconds(pollInterval)); 156 debug("Polling sensors for {NAME} in {INTERVAL} seconds", "NAME", 157 config.name, "INTERVAL", pollInterval); 158 } 159 160 co_return; 161 } 162 163 static auto getObjectPath(const config::Config& config, config::StatusType type, 164 const std::string& name) 165 -> sdbusplus::message::object_path 166 { 167 switch (type) 168 { 169 case config::StatusType::sensorReadingCritical: 170 case config::StatusType::sensorReadingWarning: 171 case config::StatusType::sensorFailure: 172 return sdbusplus::message::object_path( 173 std::string(SensorValueIntf::namespace_path::value) + "/" + 174 name); 175 case config::StatusType::controllerFailure: 176 return config.inventoryPath; 177 case config::StatusType::pumpFailure: 178 return sdbusplus::message::object_path( 179 "/xyz/openbmc_project/state/pump/" + name); 180 case config::StatusType::filterFailure: 181 return sdbusplus::message::object_path( 182 "/xyz/openbmc_project/state/filter/" + name); 183 case config::StatusType::powerFault: 184 return sdbusplus::message::object_path( 185 "/xyz/openbmc_project/state/power_rail/" + name); 186 case config::StatusType::fanFailure: 187 return sdbusplus::message::object_path( 188 "/xyz/openbmc_project/state/fan/" + name); 189 case config::StatusType::leakDetectedCritical: 190 case config::StatusType::leakDetectedWarning: 191 using DetectorIntf = 192 sdbusplus::aserver::xyz::openbmc_project::state::leak::Detector< 193 Device>; 194 return sdbusplus::message::object_path( 195 std::string(DetectorIntf::namespace_path::value) + "/" + 196 DetectorIntf::namespace_path::detector + "/" + name); 197 case config::StatusType::unknown: 198 error("Unknown status type for {NAME}", "NAME", name); 199 } 200 201 return sdbusplus::message::object_path(); 202 } 203 204 auto BaseDevice::readStatusRegisters() -> sdbusplus::async::task<void> 205 { 206 for (const auto& [address, statusBits] : config.statusRegisters) 207 { 208 static constexpr auto maxRegisterSize = 1; 209 auto registers = std::vector<uint16_t>(maxRegisterSize); 210 211 auto ret = co_await serialPort.readHoldingRegisters( 212 config.address, address, config.baudRate, config.parity, registers); 213 if (!ret) 214 { 215 error("Failed to read holding registers for {DEVICE_ADDRESS}", 216 "DEVICE_ADDRESS", config.address); 217 continue; 218 } 219 220 for (const auto& statusBit : statusBits) 221 { 222 static constexpr auto maxBitPoistion = 15; 223 if (statusBit.bitPosition > maxBitPoistion) 224 { 225 error("Invalid status bit position {POSITION} for {NAME}", 226 "POSITION", statusBit.bitPosition, "NAME", 227 statusBit.name); 228 continue; 229 } 230 auto statusBitValue = 231 ((registers[0] & (1 << statusBit.bitPosition)) != 0); 232 auto statusAsserted = (statusBitValue == statusBit.value); 233 auto objectPath = 234 getObjectPath(config, statusBit.type, statusBit.name); 235 double sensorValue = std::numeric_limits<double>::quiet_NaN(); 236 SensorValueIntf::Unit sensorUnit = SensorValueIntf::Unit::Percent; 237 auto sensorIter = sensors.find(statusBit.name); 238 if (sensorIter != sensors.end()) 239 { 240 sensorValue = sensorIter->second->value(); 241 sensorUnit = sensorIter->second->unit(); 242 } 243 244 co_await generateEvent(statusBit, objectPath, sensorValue, 245 sensorUnit, statusAsserted); 246 } 247 } 248 249 co_return; 250 } 251 252 auto BaseDevice::generateEvent( 253 const config::StatusBit& statusBit, 254 const sdbusplus::message::object_path& objectPath, double sensorValue, 255 SensorValueIntf::Unit sensorUnit, bool statusAsserted) 256 -> sdbusplus::async::task<void> 257 { 258 switch (statusBit.type) 259 { 260 case config::StatusType::sensorReadingCritical: 261 co_await events.generateSensorReadingEvent( 262 objectPath, EventIntf::EventLevel::critical, sensorValue, 263 sensorUnit, statusAsserted); 264 break; 265 case config::StatusType::sensorReadingWarning: 266 co_await events.generateSensorReadingEvent( 267 objectPath, EventIntf::EventLevel::warning, sensorValue, 268 sensorUnit, statusAsserted); 269 break; 270 case config::StatusType::sensorFailure: 271 co_await events.generateSensorFailureEvent(objectPath, 272 statusAsserted); 273 break; 274 case config::StatusType::controllerFailure: 275 co_await events.generateControllerFailureEvent( 276 objectPath, statusBit.name, statusAsserted); 277 break; 278 case config::StatusType::powerFault: 279 co_await events.generatePowerFaultEvent(objectPath, statusBit.name, 280 statusAsserted); 281 break; 282 case config::StatusType::filterFailure: 283 co_await events.generateFilterFailureEvent(objectPath, 284 statusAsserted); 285 break; 286 case config::StatusType::pumpFailure: 287 co_await events.generatePumpFailureEvent(objectPath, 288 statusAsserted); 289 break; 290 case config::StatusType::fanFailure: 291 co_await events.generateFanFailureEvent(objectPath, statusAsserted); 292 break; 293 case config::StatusType::leakDetectedCritical: 294 co_await events.generateLeakDetectedEvent( 295 objectPath, EventIntf::EventLevel::critical, statusAsserted); 296 break; 297 case config::StatusType::leakDetectedWarning: 298 co_await events.generateLeakDetectedEvent( 299 objectPath, EventIntf::EventLevel::warning, statusAsserted); 300 break; 301 case config::StatusType::unknown: 302 error("Unknown status type for {NAME}", "NAME", statusBit.name); 303 break; 304 } 305 } 306 307 } // namespace phosphor::modbus::rtu::device 308