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/common/events.cpp b/common/events.cpp
new file mode 100644
index 0000000..9c61365
--- /dev/null
+++ b/common/events.cpp
@@ -0,0 +1,342 @@
+#include "events.hpp"
+
+#include <phosphor-logging/commit.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async.hpp>
+#include <xyz/openbmc_project/Sensor/Threshold/event.hpp>
+#include <xyz/openbmc_project/Sensor/event.hpp>
+#include <xyz/openbmc_project/State/Fan/event.hpp>
+#include <xyz/openbmc_project/State/Filter/event.hpp>
+#include <xyz/openbmc_project/State/Leak/Detector/event.hpp>
+#include <xyz/openbmc_project/State/Power/event.hpp>
+#include <xyz/openbmc_project/State/Pump/event.hpp>
+#include <xyz/openbmc_project/State/SMC/event.hpp>
+
+namespace phosphor::modbus::events
+{
+
+PHOSPHOR_LOG2_USING;
+
+const std::unordered_map<EventLevel, std::string> eventLevelToName = {
+    {EventLevel::critical, "Critical"},
+    {EventLevel::warning, "Warning"},
+};
+
+auto Events::generateSensorReadingEvent(
+    sdbusplus::message::object_path objectPath, EventLevel level, double value,
+    SensorValueIntf::Unit unit, bool asserted) -> sdbusplus::async::task<>
+{
+    namespace error_intf =
+        sdbusplus::error::xyz::openbmc_project::sensor::Threshold;
+    namespace event_intf =
+        sdbusplus::event::xyz::openbmc_project::sensor::Threshold;
+
+    auto eventName =
+        objectPath.str + ".threshold." + eventLevelToName.at(level);
+    auto pendingEvent = pendingEvents.find(eventName);
+
+    if (asserted)
+    {
+        if (pendingEvent == pendingEvents.end())
+        {
+            sdbusplus::message::object_path eventPath{};
+            if (level == EventLevel::critical)
+            {
+                eventPath = co_await lg2::commit(
+                    ctx, error_intf::ReadingCritical("SENSOR_NAME", objectPath,
+                                                     "READING_VALUE", value,
+                                                     "UNITS", unit));
+            }
+            else
+            {
+                eventPath = co_await lg2::commit(
+                    ctx, error_intf::ReadingWarning("SENSOR_NAME", objectPath,
+                                                    "READING_VALUE", value,
+                                                    "UNITS", unit));
+            }
+
+            pendingEvents.emplace(eventName, eventPath);
+        }
+    }
+    else
+    {
+        if (pendingEvent != pendingEvents.end())
+        {
+            co_await lg2::resolve(ctx, pendingEvent->second);
+
+            co_await lg2::commit(ctx,
+                                 event_intf::SensorReadingNormalRange(
+                                     "SENSOR_NAME", objectPath, "READING_VALUE",
+                                     value, "UNITS", unit));
+
+            pendingEvents.erase(eventName);
+        }
+    }
+
+    debug("Sensor reading event for {EVENT_NAME} is {STATUS}", "EVENT_NAME",
+          eventName, "STATUS", (asserted ? "asserted" : "deasserted"));
+}
+
+auto Events::generateSensorFailureEvent(
+    sdbusplus::message::object_path objectPath, bool asserted)
+    -> sdbusplus::async::task<>
+{
+    namespace error_intf = sdbusplus::error::xyz::openbmc_project::Sensor;
+    namespace event_intf = sdbusplus::event::xyz::openbmc_project::Sensor;
+
+    auto eventName = objectPath.str + ".SensorFailure";
+    auto pendingEvent = pendingEvents.find(eventName);
+
+    if (asserted)
+    {
+        if (pendingEvent == pendingEvents.end())
+        {
+            auto eventPath = co_await lg2::commit(
+                ctx, error_intf::SensorFailure("SENSOR_NAME", objectPath));
+            pendingEvents.emplace(eventName, eventPath);
+        }
+    }
+    else
+    {
+        if (pendingEvent != pendingEvents.end())
+        {
+            co_await lg2::resolve(ctx, pendingEvent->second);
+
+            co_await lg2::commit(
+                ctx, event_intf::SensorRestored("SENSOR_NAME", objectPath));
+            pendingEvents.erase(eventName);
+        }
+    }
+
+    debug("Sensor failure event for {EVENT_NAME} is {STATUS}", "EVENT_NAME",
+          eventName, "STATUS", (asserted ? "asserted" : "deasserted"));
+}
+
+auto Events::generateControllerFailureEvent(
+    sdbusplus::message::object_path objectPath, std::string additionalInfo,
+    bool asserted) -> sdbusplus::async::task<>
+{
+    namespace error_intf = sdbusplus::error::xyz::openbmc_project::state::SMC;
+    namespace event_intf = sdbusplus::event::xyz::openbmc_project::state::SMC;
+
+    auto eventName = objectPath.str + ".ControllerFailure." + additionalInfo;
+    auto pendingEvent = pendingEvents.find(eventName);
+
+    if (asserted)
+    {
+        if (pendingEvent == pendingEvents.end())
+        {
+            auto eventPath = co_await lg2::commit(
+                ctx, error_intf::SMCFailed("IDENTIFIER", objectPath,
+                                           "FAILURE_TYPE", additionalInfo));
+            pendingEvents.emplace(eventName, eventPath);
+        }
+    }
+    else
+    {
+        if (pendingEvent != pendingEvents.end())
+        {
+            co_await lg2::resolve(ctx, pendingEvent->second);
+
+            co_await lg2::commit(
+                ctx, event_intf::SMCRestored("IDENTIFIER", objectPath));
+
+            pendingEvents.erase(eventName);
+        }
+    }
+
+    debug("Controller failure event for {EVENT_NAME} is {STATUS}", "EVENT_NAME",
+          eventName, "STATUS", (asserted ? "asserted" : "deasserted"));
+}
+
+auto Events::generatePowerFaultEvent(sdbusplus::message::object_path objectPath,
+                                     std::string additionalInfo, bool asserted)
+    -> sdbusplus::async::task<>
+{
+    namespace error_intf = sdbusplus::error::xyz::openbmc_project::state::Power;
+    namespace event_intf = sdbusplus::event::xyz::openbmc_project::state::Power;
+
+    auto eventName = objectPath.str + ".PowerFailure." + additionalInfo;
+    auto pendingEvent = pendingEvents.find(eventName);
+
+    if (asserted)
+    {
+        if (pendingEvent == pendingEvents.end())
+        {
+            auto eventPath = co_await lg2::commit(
+                ctx,
+                error_intf::PowerRailFault("POWER_RAIL", objectPath,
+                                           "FAILURE_DATA", additionalInfo));
+            pendingEvents.emplace(eventName, eventPath);
+        }
+    }
+    else
+    {
+        if (pendingEvent != pendingEvents.end())
+        {
+            co_await lg2::resolve(ctx, pendingEvent->second);
+
+            co_await lg2::commit(ctx, event_intf::PowerRailFaultRecovered(
+                                          "POWER_RAIL", objectPath));
+
+            pendingEvents.erase(eventName);
+        }
+    }
+
+    debug("Power fault event for {EVENT_NAME} is {STATUS}", "EVENT_NAME",
+          eventName, "STATUS", (asserted ? "asserted" : "deasserted"));
+}
+
+auto Events::generateFilterFailureEvent(
+    sdbusplus::message::object_path objectPath, bool asserted)
+    -> sdbusplus::async::task<>
+{
+    namespace error_intf =
+        sdbusplus::error::xyz::openbmc_project::state::Filter;
+    namespace event_intf =
+        sdbusplus::event::xyz::openbmc_project::state::Filter;
+
+    auto eventName = objectPath.str + ".FilterFailure";
+    auto pendingEvent = pendingEvents.find(eventName);
+
+    if (asserted)
+    {
+        if (pendingEvent == pendingEvents.end())
+        {
+            auto eventPath = co_await lg2::commit(
+                ctx,
+                error_intf::FilterRequiresService("FILTER_NAME", objectPath));
+            pendingEvents.emplace(eventName, eventPath);
+        }
+    }
+    else
+    {
+        if (pendingEvent != pendingEvents.end())
+        {
+            co_await lg2::resolve(ctx, pendingEvent->second);
+
+            co_await lg2::commit(
+                ctx, event_intf::FilterRestored("FILTER_NAME", objectPath));
+
+            pendingEvents.erase(eventName);
+        }
+    }
+
+    debug("Filter failure event for {EVENT_NAME} is {STATUS}", "EVENT_NAME",
+          eventName, "STATUS", (asserted ? "asserted" : "deasserted"));
+}
+
+auto Events::generatePumpFailureEvent(
+    sdbusplus::message::object_path objectPath, bool asserted)
+    -> sdbusplus::async::task<>
+{
+    namespace error_intf = sdbusplus::error::xyz::openbmc_project::state::Pump;
+    namespace event_intf = sdbusplus::event::xyz::openbmc_project::state::Pump;
+
+    auto eventName = objectPath.str + ".PumpFailure";
+    auto pendingEvent = pendingEvents.find(eventName);
+
+    if (asserted)
+    {
+        if (pendingEvent == pendingEvents.end())
+        {
+            auto eventPath = co_await lg2::commit(
+                ctx, error_intf::PumpFailed("PUMP_NAME", objectPath));
+            pendingEvents.emplace(eventName, eventPath);
+        }
+    }
+    else
+    {
+        if (pendingEvent != pendingEvents.end())
+        {
+            co_await lg2::resolve(ctx, pendingEvent->second);
+
+            co_await lg2::commit(
+                ctx, event_intf::PumpRestored("PUMP_NAME", objectPath));
+
+            pendingEvents.erase(eventName);
+        }
+    }
+
+    debug("Pump failure event for {EVENT_NAME} is {STATUS}", "EVENT_NAME",
+          eventName, "STATUS", (asserted ? "asserted" : "deasserted"));
+}
+
+auto Events::generateFanFailureEvent(sdbusplus::message::object_path objectPath,
+                                     bool asserted) -> sdbusplus::async::task<>
+{
+    namespace error_intf = sdbusplus::error::xyz::openbmc_project::state::Fan;
+    namespace event_intf = sdbusplus::event::xyz::openbmc_project::state::Fan;
+
+    auto eventName = objectPath.str + ".FanFailure";
+    auto pendingEvent = pendingEvents.find(eventName);
+
+    if (asserted)
+    {
+        if (pendingEvent == pendingEvents.end())
+        {
+            auto eventPath = co_await lg2::commit(
+                ctx, error_intf::FanFailed("FAN_NAME", objectPath));
+            pendingEvents.emplace(eventName, eventPath);
+        }
+    }
+    else
+    {
+        if (pendingEvent != pendingEvents.end())
+        {
+            co_await lg2::resolve(ctx, pendingEvent->second);
+
+            co_await lg2::commit(
+                ctx, event_intf::FanRestored("FAN_NAME", objectPath));
+
+            pendingEvents.erase(eventName);
+        }
+    }
+
+    debug("Fan failure event for {EVENT_NAME} is {STATUS}", "EVENT_NAME",
+          eventName, "STATUS", (asserted ? "asserted" : "deasserted"));
+}
+
+auto Events::generateLeakDetectedEvent(
+    sdbusplus::message::object_path objectPath, EventLevel level, bool asserted)
+    -> sdbusplus::async::task<>
+{
+    auto eventName = objectPath.str + ".Leak." + eventLevelToName.at(level);
+
+    if (!asserted)
+    {
+        auto pendingEvent = pendingEvents.find(eventName);
+        if (pendingEvent != pendingEvents.end())
+        {
+            co_await lg2::resolve(ctx, pendingEvent->second);
+
+            using DetectorNormal = sdbusplus::event::xyz::openbmc_project::
+                state::leak::Detector::LeakDetectedNormal;
+            co_await lg2::commit(ctx,
+                                 DetectorNormal("DETECTOR_NAME", objectPath));
+
+            pendingEvents.erase(eventName);
+        }
+        co_return;
+    }
+
+    namespace error_intf =
+        sdbusplus::error::xyz::openbmc_project::state::leak::Detector;
+    sdbusplus::message::object_path eventPath{};
+
+    if (level == EventLevel::critical)
+    {
+        eventPath = co_await lg2::commit(
+            ctx, error_intf::LeakDetectedCritical("DETECTOR_NAME", objectPath));
+        error("Critical leak detected for {PATH}", "PATH", objectPath);
+    }
+    else
+    {
+        eventPath = co_await lg2::commit(
+            ctx, error_intf::LeakDetectedWarning("DETECTOR_NAME", objectPath));
+        warning("Warning leak detected for {PATH}", "PATH", objectPath);
+    }
+    pendingEvents[eventName] = eventPath;
+}
+
+} // namespace phosphor::modbus::events
diff --git a/common/events.hpp b/common/events.hpp
new file mode 100644
index 0000000..8a13f82
--- /dev/null
+++ b/common/events.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <sdbusplus/async.hpp>
+#include <xyz/openbmc_project/Sensor/Value/client.hpp>
+
+namespace phosphor::modbus::events
+{
+
+using SensorValueIntf =
+    sdbusplus::client::xyz::openbmc_project::sensor::Value<>;
+
+enum class EventLevel
+{
+    critical,
+    warning
+};
+
+class Events
+{
+  public:
+    Events() = delete;
+
+    explicit Events(sdbusplus::async::context& ctx) : ctx(ctx) {}
+
+    auto generateSensorReadingEvent(sdbusplus::message::object_path objectPath,
+                                    EventLevel level, double value,
+                                    SensorValueIntf::Unit unit, bool asserted)
+        -> sdbusplus::async::task<>;
+
+    auto generateSensorFailureEvent(sdbusplus::message::object_path objectPath,
+                                    bool asserted) -> sdbusplus::async::task<>;
+
+    auto generateControllerFailureEvent(
+        sdbusplus::message::object_path objectPath, std::string additionalInfo,
+        bool asserted) -> sdbusplus::async::task<>;
+
+    auto generatePowerFaultEvent(sdbusplus::message::object_path objectPath,
+                                 std::string additionalInfo, bool asserted)
+        -> sdbusplus::async::task<>;
+
+    auto generateFilterFailureEvent(sdbusplus::message::object_path objectPath,
+                                    bool asserted) -> sdbusplus::async::task<>;
+
+    auto generatePumpFailureEvent(sdbusplus::message::object_path objectPath,
+                                  bool asserted) -> sdbusplus::async::task<>;
+
+    auto generateFanFailureEvent(sdbusplus::message::object_path objectPath,
+                                 bool asserted) -> sdbusplus::async::task<>;
+
+    auto generateLeakDetectedEvent(sdbusplus::message::object_path objectPath,
+                                   EventLevel level, bool asserted)
+        -> sdbusplus::async::task<>;
+
+    // Button Pressed
+
+  private:
+    /** @brief Map type for event name to log event object path */
+    using event_map_t = std::map<std::string, sdbusplus::message::object_path>;
+
+    sdbusplus::async::context& ctx;
+    event_map_t pendingEvents;
+};
+
+} // namespace phosphor::modbus::events
diff --git a/common/meson.build b/common/meson.build
index cd17e78..8e40d37 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -1,6 +1,7 @@
 modbus_common_lib = static_library(
     'modbus_common_lib',
     'entity_manager_interface.cpp',
+    'events.cpp',
     include_directories: ['.'],
     dependencies: [default_deps],
 )