#include "base_device.hpp" #include #include #include namespace phosphor::modbus::rtu::device { PHOSPHOR_LOG2_USING; BaseDevice::BaseDevice(sdbusplus::async::context& ctx, const config::Config& config, PortIntf& serialPort, EventIntf::Events& events) : ctx(ctx), config(config), serialPort(serialPort), events(events) { createSensors(); if (!config.firmwareRegisters.empty()) { currentFirmware = std::make_unique(ctx, config, serialPort); ctx.spawn(currentFirmware->readVersionRegister()); } info("Successfully created device {NAME}", "NAME", config.name); } static auto getObjectPath(const std::string& sensorType, const std::string& sensorName) -> sdbusplus::message::object_path { return sdbusplus::message::object_path( std::string(SensorValueIntf::namespace_path::value) + "/" + sensorType + "/" + sensorName); } auto BaseDevice::createSensors() -> void { for (const auto& sensorRegister : config.sensorRegisters) { SensorValueIntf::properties_t initProperties = { std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), sensorRegister.unit}; auto sensorPath = getObjectPath( sensorRegister.pathSuffix, config.name + "_" + sensorRegister.name); auto sensor = std::make_unique( ctx, sensorPath.str.c_str(), initProperties); sensor->emit_added(); sensors.emplace(sensorRegister.name, std::move(sensor)); } return; } static auto getRawIntegerFromRegister(const std::vector& reg, bool sign) -> int64_t { if (reg.empty()) { return 0; } uint64_t accumulator = 0; for (auto val : reg) { accumulator = (accumulator << 16) | val; } int64_t result = 0; if (sign) { if (reg.size() == 1) { result = static_cast(accumulator); } else if (reg.size() == 2) { result = static_cast(accumulator); } else { result = static_cast(accumulator); } } else { if (reg.size() == 1) { result = static_cast(accumulator); } else if (reg.size() == 2) { result = static_cast(accumulator); } else { result = static_cast(accumulator); } } return result; } auto BaseDevice::readSensorRegisters() -> sdbusplus::async::task { while (!ctx.stop_requested()) { for (const auto& sensorRegister : config.sensorRegisters) { auto sensor = sensors.find(sensorRegister.name); if (sensor == sensors.end()) { error("Sensor not found for {NAME}", "NAME", sensorRegister.name); continue; } if (sensorRegister.size > 4) { error("Unsupported size for register {NAME}", "NAME", sensorRegister.name); continue; } auto registers = std::vector(sensorRegister.size); auto ret = co_await serialPort.readHoldingRegisters( config.address, sensorRegister.offset, config.baudRate, config.parity, registers); if (!ret) { error( "Failed to read holding registers {NAME} for {DEVICE_ADDRESS}", "NAME", sensorRegister.name, "DEVICE_ADDRESS", config.address); continue; } double regVal = static_cast( getRawIntegerFromRegister(registers, sensorRegister.isSigned)); if (sensorRegister.format == config::SensorFormat::floatingPoint) { regVal = sensorRegister.shift + (sensorRegister.scale * (regVal / (1ULL << sensorRegister.precision))); } sensor->second->value(regVal); } co_await readStatusRegisters(); constexpr auto pollInterval = 3; co_await sdbusplus::async::sleep_for( ctx, std::chrono::seconds(pollInterval)); debug("Polling sensors for {NAME} in {INTERVAL} seconds", "NAME", config.name, "INTERVAL", pollInterval); } co_return; } static auto getObjectPath(const config::Config& config, config::StatusType type, const std::string& name) -> sdbusplus::message::object_path { switch (type) { case config::StatusType::sensorReadingCritical: case config::StatusType::sensorReadingWarning: case config::StatusType::sensorFailure: return sdbusplus::message::object_path( std::string(SensorValueIntf::namespace_path::value) + "/" + name); case config::StatusType::controllerFailure: return config.inventoryPath; case config::StatusType::pumpFailure: return sdbusplus::message::object_path( "/xyz/openbmc_project/state/pump/" + name); case config::StatusType::filterFailure: return sdbusplus::message::object_path( "/xyz/openbmc_project/state/filter/" + name); case config::StatusType::powerFault: return sdbusplus::message::object_path( "/xyz/openbmc_project/state/power_rail/" + name); case config::StatusType::fanFailure: return sdbusplus::message::object_path( "/xyz/openbmc_project/state/fan/" + name); case config::StatusType::leakDetectedCritical: case config::StatusType::leakDetectedWarning: using DetectorIntf = sdbusplus::aserver::xyz::openbmc_project::state::leak::Detector< Device>; return sdbusplus::message::object_path( std::string(DetectorIntf::namespace_path::value) + "/" + DetectorIntf::namespace_path::detector + "/" + name); case config::StatusType::unknown: error("Unknown status type for {NAME}", "NAME", name); } return sdbusplus::message::object_path(); } auto BaseDevice::readStatusRegisters() -> sdbusplus::async::task { for (const auto& [address, statusBits] : config.statusRegisters) { static constexpr auto maxRegisterSize = 1; auto registers = std::vector(maxRegisterSize); auto ret = co_await serialPort.readHoldingRegisters( config.address, address, config.baudRate, config.parity, registers); if (!ret) { error("Failed to read holding registers for {DEVICE_ADDRESS}", "DEVICE_ADDRESS", config.address); continue; } for (const auto& statusBit : statusBits) { static constexpr auto maxBitPoistion = 15; if (statusBit.bitPosition > maxBitPoistion) { error("Invalid status bit position {POSITION} for {NAME}", "POSITION", statusBit.bitPosition, "NAME", statusBit.name); continue; } auto statusBitValue = ((registers[0] & (1 << statusBit.bitPosition)) != 0); auto statusAsserted = (statusBitValue == statusBit.value); auto objectPath = getObjectPath(config, statusBit.type, statusBit.name); double sensorValue = std::numeric_limits::quiet_NaN(); SensorValueIntf::Unit sensorUnit = SensorValueIntf::Unit::Percent; auto sensorIter = sensors.find(statusBit.name); if (sensorIter != sensors.end()) { sensorValue = sensorIter->second->value(); sensorUnit = sensorIter->second->unit(); } co_await generateEvent(statusBit, objectPath, sensorValue, sensorUnit, statusAsserted); } } co_return; } auto BaseDevice::generateEvent( const config::StatusBit& statusBit, const sdbusplus::message::object_path& objectPath, double sensorValue, SensorValueIntf::Unit sensorUnit, bool statusAsserted) -> sdbusplus::async::task { switch (statusBit.type) { case config::StatusType::sensorReadingCritical: co_await events.generateSensorReadingEvent( objectPath, EventIntf::EventLevel::critical, sensorValue, sensorUnit, statusAsserted); break; case config::StatusType::sensorReadingWarning: co_await events.generateSensorReadingEvent( objectPath, EventIntf::EventLevel::warning, sensorValue, sensorUnit, statusAsserted); break; case config::StatusType::sensorFailure: co_await events.generateSensorFailureEvent(objectPath, statusAsserted); break; case config::StatusType::controllerFailure: co_await events.generateControllerFailureEvent( objectPath, statusBit.name, statusAsserted); break; case config::StatusType::powerFault: co_await events.generatePowerFaultEvent(objectPath, statusBit.name, statusAsserted); break; case config::StatusType::filterFailure: co_await events.generateFilterFailureEvent(objectPath, statusAsserted); break; case config::StatusType::pumpFailure: co_await events.generatePumpFailureEvent(objectPath, statusAsserted); break; case config::StatusType::fanFailure: co_await events.generateFanFailureEvent(objectPath, statusAsserted); break; case config::StatusType::leakDetectedCritical: co_await events.generateLeakDetectedEvent( objectPath, EventIntf::EventLevel::critical, statusAsserted); break; case config::StatusType::leakDetectedWarning: co_await events.generateLeakDetectedEvent( objectPath, EventIntf::EventLevel::warning, statusAsserted); break; case config::StatusType::unknown: error("Unknown status type for {NAME}", "NAME", statusBit.name); break; } } } // namespace phosphor::modbus::rtu::device