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();