rtu: add read status register and event support
Add support to read the status registers and generate the appropriate
events.
Tested:
Unit Test -
```
> meson test -t 10 -C builddir/ --print-errorlogs --wrapper="valgrind --error-exitcode=1" test_events
ninja: Entering directory `/host/repos/Modbus/phosphor-modbus/builddir'
ninja: no work to do.
1/1 test_events OK 9.69s
Ok: 1
Fail: 0
```
Tested on Qemu -
```
Apr 03 15:41:52 ventura phosphor-modbus-rtu[1654]: OPENBMC_MESSAGE_ID={"xyz.openbmc_project.Sensor.SensorFailure":{"SENSOR_NAME":"/xyz/openbmc_project/sensors/RPU_Coolant_Outlet_Thermometer_Status","_SOURCE":{"COLUMN":73,"FILE":"../git/common/ev
ents.cpp","FUNCTION":"sdbusplus::async::task<> phosphor::modbus::events::Events::generateSensorFailureEvent(sdbusplus::message::object_path, bool)","LINE":95,"PID":1654}}}
...
Apr 03 15:41:52 ventura phosphor-modbus-rtu[1654]: OPENBMC_MESSAGE_ID={"xyz.openbmc_project.Sensor.Threshold.ReadingCritical":{"READING_VALUE":1670.6000000000001,"SENSOR_NAME":"/xyz/openbmc_project/sensors/RPU_Coolant_Outlet_Temp_C","UNITS":"xyz
.openbmc_project.Sensor.Value.Unit.DegreesC","_SOURCE":{"COLUMN":67,"FILE":"../git/common/events.cpp","FUNCTION":"sdbusplus::async::task<> phosphor::modbus::events::Events::generateSensorReadingEvent(sdbusplus::message::object_path, phosphor::modbus::events::EventLevel, double, sdbusplus::common::xyz::openbmc_project::sensor::Value::Unit, bool)","LINE":48,"PID":1654}}}
```
Change-Id: Icd78f22cf07798d06916cc077ec3f8bfac9ee8d3
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/rtu/device/base_device.cpp b/rtu/device/base_device.cpp
index 73d1ca6..c075d55 100644
--- a/rtu/device/base_device.cpp
+++ b/rtu/device/base_device.cpp
@@ -1,6 +1,7 @@
#include "base_device.hpp"
#include <phosphor-logging/lg2.hpp>
+#include <xyz/openbmc_project/State/Leak/Detector/aserver.hpp>
#include <numeric>
@@ -10,8 +11,9 @@
PHOSPHOR_LOG2_USING;
BaseDevice::BaseDevice(sdbusplus::async::context& ctx,
- const config::Config& config, PortIntf& serialPort) :
- ctx(ctx), config(config), serialPort(serialPort)
+ const config::Config& config, PortIntf& serialPort,
+ EventIntf::Events& events) :
+ ctx(ctx), config(config), serialPort(serialPort), events(events)
{
createSensors();
@@ -146,6 +148,8 @@
sensor->second->value(regVal);
}
+ co_await readStatusRegisters();
+
constexpr auto pollInterval = 3;
co_await sdbusplus::async::sleep_for(
ctx, std::chrono::seconds(pollInterval));
@@ -156,4 +160,148 @@
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<void>
+{
+ for (const auto& [address, statusBits] : config.statusRegisters)
+ {
+ static constexpr auto maxRegisterSize = 1;
+ auto registers = std::vector<uint16_t>(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<double>::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<void>
+{
+ 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
diff --git a/rtu/device/base_device.hpp b/rtu/device/base_device.hpp
index a64d708..3a2f0a8 100644
--- a/rtu/device/base_device.hpp
+++ b/rtu/device/base_device.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "base_config.hpp"
+#include "common/events.hpp"
#include "modbus/modbus.hpp"
#include "port/base_port.hpp"
@@ -15,6 +16,7 @@
using SensorValueIntf =
sdbusplus::aserver::xyz::openbmc_project::sensor::Value<Device>;
using PortIntf = phosphor::modbus::rtu::port::BasePort;
+namespace EventIntf = phosphor::modbus::events;
class BaseDevice
{
@@ -22,18 +24,27 @@
BaseDevice() = delete;
explicit BaseDevice(sdbusplus::async::context& ctx,
- const config::Config& config, PortIntf& serialPort);
+ const config::Config& config, PortIntf& serialPort,
+ EventIntf::Events& events);
auto readSensorRegisters() -> sdbusplus::async::task<void>;
private:
auto createSensors() -> void;
+ auto readStatusRegisters() -> sdbusplus::async::task<void>;
+
+ auto generateEvent(const config::StatusBit& statusBit,
+ const sdbusplus::message::object_path& objectPath,
+ double sensorValue, SensorValueIntf::Unit sensorUnit,
+ bool statusAsserted) -> sdbusplus::async::task<void>;
+
using sensors_map_t =
std::unordered_map<std::string, std::unique_ptr<SensorValueIntf>>;
sdbusplus::async::context& ctx;
const config::Config config;
PortIntf& serialPort;
+ EventIntf::Events& events;
sensors_map_t sensors;
};
diff --git a/rtu/device/device_factory.cpp b/rtu/device/device_factory.cpp
index d707952..3efdbac 100644
--- a/rtu/device/device_factory.cpp
+++ b/rtu/device/device_factory.cpp
@@ -38,12 +38,14 @@
auto DeviceFactory::create(sdbusplus::async::context& ctx,
const config::DeviceFactoryConfig& config,
- PortIntf& serialPort) -> std::unique_ptr<BaseDevice>
+ PortIntf& serialPort, EventIntf::Events& events)
+ -> std::unique_ptr<BaseDevice>
{
switch (config.deviceType)
{
case config::DeviceType::reservoirPumpUnit:
- return std::make_unique<ReservoirPumpUnit>(ctx, config, serialPort);
+ return std::make_unique<ReservoirPumpUnit>(ctx, config, serialPort,
+ events);
default:
break;
}
diff --git a/rtu/device/device_factory.hpp b/rtu/device/device_factory.hpp
index 5dca705..ca37b7b 100644
--- a/rtu/device/device_factory.hpp
+++ b/rtu/device/device_factory.hpp
@@ -44,7 +44,8 @@
static auto create(sdbusplus::async::context& ctx,
const config::DeviceFactoryConfig& config,
- PortIntf& serialPort) -> std::unique_ptr<BaseDevice>;
+ PortIntf& serialPort, EventIntf::Events& events)
+ -> std::unique_ptr<BaseDevice>;
};
} // namespace phosphor::modbus::rtu::device
diff --git a/rtu/device/reservoir_pump_unit.cpp b/rtu/device/reservoir_pump_unit.cpp
index 58debee..e4f8ae8 100644
--- a/rtu/device/reservoir_pump_unit.cpp
+++ b/rtu/device/reservoir_pump_unit.cpp
@@ -16,10 +16,10 @@
validDevices = {{ModbusRDF040DSS5193E0ReservoirPumpUnitInterface,
config::DeviceModel::RDF040DSS5193E0}};
-ReservoirPumpUnit::ReservoirPumpUnit(sdbusplus::async::context& ctx,
- const config::Config& config,
- PortIntf& serialPort) :
- BaseDevice(ctx, config, serialPort)
+ReservoirPumpUnit::ReservoirPumpUnit(
+ sdbusplus::async::context& ctx, const config::Config& config,
+ PortIntf& serialPort, EventIntf::Events& events) :
+ BaseDevice(ctx, config, serialPort, events)
{
info("Reservoir pump unit {NAME} created successfully", "NAME",
config.name);
diff --git a/rtu/device/reservoir_pump_unit.hpp b/rtu/device/reservoir_pump_unit.hpp
index 19240f0..b3a51a5 100644
--- a/rtu/device/reservoir_pump_unit.hpp
+++ b/rtu/device/reservoir_pump_unit.hpp
@@ -19,7 +19,7 @@
public:
explicit ReservoirPumpUnit(sdbusplus::async::context& ctx,
const config::Config& config,
- PortIntf& serialPort);
+ PortIntf& serialPort, EventIntf::Events& events);
static auto getInterfaces() -> std::unordered_set<std::string>;