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/tests/meson.build b/tests/meson.build
index 8b1df07..c457029 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -70,6 +70,18 @@
'test_sensors.cpp',
'modbus_server_tester.cpp',
device_src,
+ link_with: [modbus_common_lib],
+ dependencies: [gtest_dep, gmock_dep, default_deps, modbus_rtu_dep],
+ include_directories: ['.', common_include],
+ ),
+)
+
+test(
+ 'test_events',
+ executable(
+ 'test_events',
+ 'test_events.cpp',
+ link_with: [modbus_common_lib],
dependencies: [gtest_dep, gmock_dep, default_deps, modbus_rtu_dep],
include_directories: ['.', common_include],
),
diff --git a/tests/test_events.cpp b/tests/test_events.cpp
new file mode 100644
index 0000000..1cffd2f
--- /dev/null
+++ b/tests/test_events.cpp
@@ -0,0 +1,299 @@
+#include "common/events.hpp"
+
+#include <xyz/openbmc_project/Logging/Create/aserver.hpp>
+#include <xyz/openbmc_project/Logging/Entry/aserver.hpp>
+#include <xyz/openbmc_project/Sensor/Threshold/event.hpp>
+#include <xyz/openbmc_project/Sensor/event.hpp>
+#include <xyz/openbmc_project/State/Leak/Detector/event.hpp>
+#include <xyz/openbmc_project/State/Power/event.hpp>
+#include <xyz/openbmc_project/State/SMC/event.hpp>
+
+#include <gtest/gtest.h>
+
+class TestEventServer;
+class TestEventEntry;
+
+using namespace std::literals;
+namespace EventIntf = phosphor::modbus::events;
+using EventServerIntf =
+ sdbusplus::aserver::xyz::openbmc_project::logging::Create<TestEventServer>;
+using EventEntryIntf =
+ sdbusplus::aserver::xyz::openbmc_project::logging::Entry<TestEventEntry>;
+
+namespace SensorThresholdErrorIntf =
+ sdbusplus::error::xyz::openbmc_project::sensor::Threshold;
+namespace SensorThresholdEventIntf =
+ sdbusplus::event::xyz::openbmc_project::sensor::Threshold;
+namespace SensorErrorIntf = sdbusplus::error::xyz::openbmc_project::Sensor;
+namespace SensorEventIntf = sdbusplus::event::xyz::openbmc_project::Sensor;
+namespace ControllerErrorIntf =
+ sdbusplus::error::xyz::openbmc_project::state::SMC;
+namespace ControllerEventIntf =
+ sdbusplus::event::xyz::openbmc_project::state::SMC;
+namespace PowerErrorIntf = sdbusplus::error::xyz::openbmc_project::state::Power;
+namespace PowerEventIntf = sdbusplus::event::xyz::openbmc_project::state::Power;
+namespace LeakErrorIntf =
+ sdbusplus::error::xyz::openbmc_project::state::leak::Detector;
+namespace LeakEventIntf =
+ sdbusplus::event::xyz::openbmc_project::state::leak::Detector;
+
+// Test Event Class to mock the EventEntry
+class TestEventEntry : public EventEntryIntf
+{
+ public:
+ TestEventEntry(sdbusplus::async::context& ctx, const char* path) :
+ EventEntryIntf(ctx, path)
+ {}
+
+ auto method_call(get_entry_t)
+ -> sdbusplus::async::task<get_entry_t::return_type>
+ {
+ get_entry_t::return_type fd1 = 0;
+ co_return fd1;
+ }
+};
+
+// Test Event Server Class to mock the EventServer
+class TestEventServer : public EventServerIntf
+{
+ public:
+ TestEventServer(sdbusplus::async::context& ctx, const char* path) :
+ EventServerIntf(ctx, path), ctx(ctx)
+ {}
+
+ auto method_call(create_t, auto message, auto, auto)
+ -> sdbusplus::async::task<create_t::return_type>
+
+ {
+ static int cnt = 1;
+ cnt++;
+
+ // Append the count to the object path to make it unique for each event
+ std::string objectPath =
+ "/xyz/openbmc_project/logging/entry/TestEvent1" +
+ std::to_string(cnt);
+ EXPECT_EQ(message, expectedEvent) << "Event name mismatch";
+
+ eventEntries.emplace_back(
+ std::make_unique<TestEventEntry>(ctx, objectPath.c_str()));
+
+ co_return sdbusplus::message::object_path(objectPath);
+ }
+
+ auto method_call(create_with_ffdc_files_t, auto, auto, auto, auto)
+ -> sdbusplus::async::task<create_with_ffdc_files_t::return_type>
+
+ {
+ co_return;
+ }
+
+ std::string expectedEvent = "";
+
+ private:
+ sdbusplus::async::context& ctx;
+ std::vector<std::unique_ptr<TestEventEntry>> eventEntries;
+};
+
+class EventsTest : public ::testing::Test
+{
+ public:
+ enum class EventTestType
+ {
+ sensorWarningEvent,
+ sensorCriticalEvent,
+ sensorFailureEvent,
+ controllerFailureEvent,
+ powerFailureEvent,
+ leakWarningEvent,
+ leakCriticalEvent,
+ };
+
+ static constexpr auto sensorObjectPath =
+ "/xyz/openbmc_project/sensors/OutletTemperature";
+ static constexpr auto serviceName = "xyz.openbmc_project.Logging";
+ static constexpr auto assert = true;
+ static constexpr auto deassert = false;
+ const char* objectPath = "/xyz/openbmc_project/logging";
+ sdbusplus::async::context ctx;
+ EventIntf::Events events;
+ TestEventServer eventServer;
+ sdbusplus::server::manager_t manager;
+
+ EventsTest() :
+ events(ctx), eventServer(ctx, objectPath), manager(ctx, objectPath)
+ {
+ ctx.request_name(serviceName);
+ }
+
+ ~EventsTest() noexcept override {}
+
+ auto testAssertEvent(EventTestType eventType)
+ -> sdbusplus::async::task<void>
+ {
+ switch (eventType)
+ {
+ case EventTestType::sensorWarningEvent:
+ eventServer.expectedEvent =
+ SensorThresholdErrorIntf::ReadingWarning::errName;
+ co_await events.generateSensorReadingEvent(
+ sdbusplus::message::object_path(sensorObjectPath),
+ EventIntf::EventLevel::warning, 60,
+ EventIntf::SensorValueIntf::Unit::DegreesC, assert);
+ break;
+ case EventTestType::sensorCriticalEvent:
+ eventServer.expectedEvent =
+ SensorThresholdErrorIntf::ReadingCritical::errName;
+ co_await events.generateSensorReadingEvent(
+ sdbusplus::message::object_path(sensorObjectPath),
+ EventIntf::EventLevel::critical, 80,
+ EventIntf::SensorValueIntf::Unit::DegreesC, assert);
+ break;
+ case EventTestType::sensorFailureEvent:
+ eventServer.expectedEvent =
+ SensorErrorIntf::SensorFailure::errName;
+ co_await events.generateSensorFailureEvent(
+ sdbusplus::message::object_path(sensorObjectPath), assert);
+ break;
+ case EventTestType::controllerFailureEvent:
+ eventServer.expectedEvent =
+ ControllerErrorIntf::SMCFailed::errName;
+ co_await events.generateControllerFailureEvent(
+ sdbusplus::message::object_path(sensorObjectPath), "",
+ assert);
+ break;
+ case EventTestType::powerFailureEvent:
+ eventServer.expectedEvent =
+ PowerErrorIntf::PowerRailFault::errName;
+ co_await events.generatePowerFaultEvent(
+ sdbusplus::message::object_path(sensorObjectPath), "",
+ assert);
+ break;
+ case EventTestType::leakWarningEvent:
+ eventServer.expectedEvent =
+ LeakErrorIntf::LeakDetectedWarning::errName;
+ co_await events.generateLeakDetectedEvent(
+ sdbusplus::message::object_path(sensorObjectPath),
+ EventIntf::EventLevel::warning, assert);
+ break;
+ case EventTestType::leakCriticalEvent:
+ eventServer.expectedEvent =
+ LeakErrorIntf::LeakDetectedCritical::errName;
+ co_await events.generateLeakDetectedEvent(
+ sdbusplus::message::object_path(sensorObjectPath),
+ EventIntf::EventLevel::critical, assert);
+ break;
+ }
+ }
+
+ auto testDeassertEvent(EventTestType eventType)
+ -> sdbusplus::async::task<void>
+ {
+ switch (eventType)
+ {
+ case EventTestType::sensorWarningEvent:
+ eventServer.expectedEvent =
+ SensorThresholdEventIntf::SensorReadingNormalRange::errName;
+ co_await events.generateSensorReadingEvent(
+ sdbusplus::message::object_path(sensorObjectPath),
+ EventIntf::EventLevel::warning, 40,
+ EventIntf::SensorValueIntf::Unit::DegreesC, deassert);
+ break;
+ case EventTestType::sensorCriticalEvent:
+ eventServer.expectedEvent =
+ SensorThresholdEventIntf::SensorReadingNormalRange::errName;
+ co_await events.generateSensorReadingEvent(
+ sdbusplus::message::object_path(sensorObjectPath),
+ EventIntf::EventLevel::critical, 40,
+ EventIntf::SensorValueIntf::Unit::DegreesC, deassert);
+ break;
+ case EventTestType::sensorFailureEvent:
+ eventServer.expectedEvent =
+ SensorEventIntf::SensorRestored::errName;
+ co_await events.generateSensorFailureEvent(
+ sdbusplus::message::object_path(sensorObjectPath),
+ deassert);
+ break;
+ case EventTestType::controllerFailureEvent:
+ eventServer.expectedEvent =
+ ControllerEventIntf::SMCRestored::errName;
+ co_await events.generateControllerFailureEvent(
+ sdbusplus::message::object_path(sensorObjectPath), "",
+ deassert);
+ break;
+ case EventTestType::powerFailureEvent:
+ eventServer.expectedEvent =
+ PowerEventIntf::PowerRailFaultRecovered::errName;
+ co_await events.generatePowerFaultEvent(
+ sdbusplus::message::object_path(sensorObjectPath), "",
+ deassert);
+ break;
+ case EventTestType::leakWarningEvent:
+ eventServer.expectedEvent =
+ LeakEventIntf::LeakDetectedNormal::errName;
+ co_await events.generateLeakDetectedEvent(
+ sdbusplus::message::object_path(sensorObjectPath),
+ EventIntf::EventLevel::warning, deassert);
+ break;
+ case EventTestType::leakCriticalEvent:
+ eventServer.expectedEvent =
+ LeakEventIntf::LeakDetectedNormal::errName;
+ co_await events.generateLeakDetectedEvent(
+ sdbusplus::message::object_path(sensorObjectPath),
+ EventIntf::EventLevel::critical, deassert);
+ break;
+ }
+ }
+
+ auto testEvents(EventTestType eventType) -> sdbusplus::async::task<void>
+ {
+ co_await testAssertEvent(eventType);
+
+ co_await sdbusplus::async::sleep_for(ctx, 1s);
+
+ co_await testDeassertEvent(eventType);
+
+ ctx.request_stop();
+ }
+};
+
+TEST_F(EventsTest, TestEventsSensorWarning)
+{
+ ctx.spawn(testEvents(EventTestType::sensorWarningEvent));
+ ctx.run();
+}
+
+TEST_F(EventsTest, TestEventsSensorCritical)
+{
+ ctx.spawn(testEvents(EventTestType::sensorCriticalEvent));
+ ctx.run();
+}
+
+TEST_F(EventsTest, TestEventsSensorFailure)
+{
+ ctx.spawn(testEvents(EventTestType::sensorFailureEvent));
+ ctx.run();
+}
+
+TEST_F(EventsTest, TestEventsControllerFailure)
+{
+ ctx.spawn(testEvents(EventTestType::controllerFailureEvent));
+ ctx.run();
+}
+
+TEST_F(EventsTest, TestEventsPowerFailure)
+{
+ ctx.spawn(testEvents(EventTestType::powerFailureEvent));
+ ctx.run();
+}
+
+TEST_F(EventsTest, TestEventsLeakWarning)
+{
+ ctx.spawn(testEvents(EventTestType::leakWarningEvent));
+ ctx.run();
+}
+
+TEST_F(EventsTest, TestEventsLeakCritical)
+{
+ ctx.spawn(testEvents(EventTestType::leakCriticalEvent));
+ ctx.run();
+}
diff --git a/tests/test_sensors.cpp b/tests/test_sensors.cpp
index a5046b0..a6574cb 100644
--- a/tests/test_sensors.cpp
+++ b/tests/test_sensors.cpp
@@ -1,3 +1,4 @@
+#include "common/events.hpp"
#include "device/device_factory.hpp"
#include "modbus_server_tester.hpp"
#include "port/base_port.hpp"
@@ -22,6 +23,7 @@
namespace PortConfigIntf = PortIntf::config;
namespace DeviceIntf = phosphor::modbus::rtu::device;
namespace DeviceConfigIntf = DeviceIntf::config;
+namespace EventIntf = phosphor::modbus::events;
class MockPort : public PortIntf::BasePort
{
@@ -134,11 +136,12 @@
DeviceConfigIntf::DeviceModel::RDF040DSS5193E0,
};
- auto mockPort =
- std::make_unique<MockPort>(ctx, portConfig, clientDevicePath);
+ EventIntf::Events events{ctx};
+
+ MockPort mockPort(ctx, portConfig, clientDevicePath);
auto device = DeviceIntf::DeviceFactory::create(
- ctx, deviceFactoryConfig, *mockPort);
+ ctx, deviceFactoryConfig, mockPort, events);
co_await device->readSensorRegisters();