Add support for Previous State in Sensor events

Previously, the sensor events generated by the PLDM stack only
included the current state of the sensor without any reference
to the previous state.

This commit introduces a new feature to address this limitation
by caching the previous states of all sensors.

The enhancement ensures that platform event messages(sensor events)
now contain the correct previous state information alongside the
current state.

Behavior Model:
1. Upon the initial occurrence of a sensor event, both the current
   state and the previous state are identical, adhering to the
   specification.
2. Subsequently, when the PLDM stack sends out sensor events, it
   retrieves the previous state from its cache and populates it
   accordingly.

Testing:
1. Example: Change the value of a sensor, such as dimm8 identify, to
   false using busctl.
   Tx: 81 02 0a 01 00 00 4d 00 01 00 [ 01 01 ]
- Note: Initially, both the event state and the previous state are 01.

2. Change the value of dimm8 identify to true.
   Tx: 81 02 0a 01 00 00 4d 00 01 00 [ 02 01 ]
- Result: Event state: 02, Previous state: 01.

3. Change the value of dimm8 identify back to false.
   Tx: 81 02 0a 01 00 00 4d 00 01 00 [ 01 02 ]
- Result: Event state: 01, Previous state: 02.

4. Similar results can be obtained using the getStateSensorReadings
   command for the same sensor at respective times.

Change-Id: Ic93a55e61fd5128cecc698b3555a6639876882ed
Signed-off-by: Manojkiran Eda <manojkiran.eda@gmail.com>
diff --git a/host-bmc/dbus_to_event_handler.cpp b/host-bmc/dbus_to_event_handler.cpp
index 55a3b9f..4b11654 100644
--- a/host-bmc/dbus_to_event_handler.cpp
+++ b/host-bmc/dbus_to_event_handler.cpp
@@ -109,10 +109,11 @@
             pldm::utils::DBusHandler::getBus(),
             propertiesChanged(dbusMapping.objectPath.c_str(),
                               dbusMapping.interface.c_str()),
-            [this, sensorEventDataVec, dbusValueMapping,
-             dbusMapping](auto& msg) mutable {
+            [this, sensorEventDataVec, dbusValueMapping, dbusMapping, sensorId,
+             offset](auto& msg) mutable {
             DbusChangedProps props{};
             std::string intf;
+            uint8_t previousState = PLDM_SENSOR_UNKNOWN;
             msg.read(intf, props);
             if (!props.contains(dbusMapping.propertyName))
             {
@@ -150,8 +151,18 @@
                         reinterpret_cast<struct pldm_sensor_event_data*>(
                             sensorEventDataVec.data());
                     eventData->event_class[1] = itr.first;
-                    eventData->event_class[2] = itr.first;
+                    if (sensorCacheMap.contains(sensorId) &&
+                        sensorCacheMap[sensorId][offset] != PLDM_SENSOR_UNKNOWN)
+                    {
+                        previousState = sensorCacheMap[sensorId][offset];
+                    }
+                    else
+                    {
+                        previousState = itr.first;
+                    }
+                    eventData->event_class[2] = previousState;
                     this->sendEventMsg(PLDM_SENSOR_EVENT, sensorEventDataVec);
+                    updateSensorCacheMaps(sensorId, offset, previousState);
                     break;
                 }
             }
diff --git a/host-bmc/dbus_to_event_handler.hpp b/host-bmc/dbus_to_event_handler.hpp
index 846f210..273a03a 100644
--- a/host-bmc/dbus_to_event_handler.hpp
+++ b/host-bmc/dbus_to_event_handler.hpp
@@ -17,6 +17,8 @@
                                   pldm::responder::pdr_utils::DbusValMaps>>;
 using sensorEvent =
     std::function<void(SensorId sensorId, const DbusObjMaps& dbusMaps)>;
+using stateSensorCacheMaps =
+    std::map<pldm::pdr::SensorID, pldm::responder::pdr_utils::EventStates>;
 
 namespace state_sensor
 {
@@ -52,6 +54,24 @@
     void listenSensorEvent(const pldm::responder::pdr_utils::Repo& repo,
                            const DbusObjMaps& dbusMaps);
 
+    /** @brief get the sensor state cache */
+    inline const stateSensorCacheMaps& getSensorCache()
+    {
+        return sensorCacheMap;
+    }
+
+    /** @brief function to update the sensor cache
+     *  @param[in] sensorId - sensor Id of the corresponding sensor
+     *  @param[in] sensorRearm - sensor rearm value with in the sensor
+     *  @param[in] previousState - previous state of the sensor
+     */
+    inline void updateSensorCacheMaps(pldm::pdr::SensorID sensorId,
+                                      size_t sensorRearm, uint8_t previousState)
+    {
+        // update the sensor cache
+        sensorCacheMap[sensorId][sensorRearm] = previousState;
+    }
+
   private:
     /** @brief Send state sensor event msg when a D-Bus property changes
      *  @param[in] sensorId - sensor id
@@ -81,6 +101,9 @@
 
     /** @brief PLDM request handler */
     pldm::requester::Handler<pldm::requester::Request>* handler;
+
+    /** @brief sensor cache */
+    stateSensorCacheMaps sensorCacheMap;
 };
 
 } // namespace state_sensor
diff --git a/libpldmresponder/pdr_utils.hpp b/libpldmresponder/pdr_utils.hpp
index 4a95ebe..44dfff3 100644
--- a/libpldmresponder/pdr_utils.hpp
+++ b/libpldmresponder/pdr_utils.hpp
@@ -79,6 +79,7 @@
 using StatestoDbusVal = std::map<State, pldm::utils::PropertyValue>;
 using DbusMappings = std::vector<pldm::utils::DBusMapping>;
 using DbusValMaps = std::vector<StatestoDbusVal>;
+using EventStates = std::array<uint8_t, 8>;
 
 /** @brief Parse PDR JSON file and output Json object
  *
diff --git a/libpldmresponder/platform.cpp b/libpldmresponder/platform.cpp
index 0e5455c..72f2977 100644
--- a/libpldmresponder/platform.cpp
+++ b/libpldmresponder/platform.cpp
@@ -788,9 +788,9 @@
     else
     {
         rc = platform_state_sensor::getStateSensorReadingsHandler<
-            pldm::utils::DBusHandler, Handler>(dBusIntf, *this, sensorId,
-                                               sensorRearmCount, comSensorCnt,
-                                               stateField);
+            pldm::utils::DBusHandler, Handler>(
+            dBusIntf, *this, sensorId, sensorRearmCount, comSensorCnt,
+            stateField, dbusToPLDMEventHandler->getSensorCache());
     }
 
     if (rc != PLDM_SUCCESS)
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
index 605c8ab..b50a1f4 100644
--- a/libpldmresponder/platform.hpp
+++ b/libpldmresponder/platform.hpp
@@ -464,6 +464,22 @@
         return fruHandler->getAssociateEntityMap();
     }
 
+    /** @brief update the sensor cache map
+     *  @param[in] sensorId - sensor id that needs an update
+     *  @param[in] sensorRearm - rearm value within the sensor
+     *  @param[in] value - value that needs to be cached
+     */
+
+    inline void updateSensorCache(pldm::pdr::SensorID sensorId,
+                                  size_t sensorRearm, uint8_t value)
+    {
+        if (dbusToPLDMEventHandler)
+        {
+            dbusToPLDMEventHandler->updateSensorCacheMaps(sensorId, sensorRearm,
+                                                          value);
+        }
+    }
+
     /** @brief process the actions that needs to be performed after a GetPDR
      *         call is received
      *  @param[in] source - sdeventplus event source
diff --git a/libpldmresponder/platform_state_sensor.hpp b/libpldmresponder/platform_state_sensor.hpp
index b57a58c..12e4b04 100644
--- a/libpldmresponder/platform_state_sensor.hpp
+++ b/libpldmresponder/platform_state_sensor.hpp
@@ -82,7 +82,8 @@
 int getStateSensorReadingsHandler(
     const DBusInterface& dBusIntf, Handler& handler, uint16_t sensorId,
     uint8_t sensorRearmCnt, uint8_t& compSensorCnt,
-    std::vector<get_sensor_state_field>& stateField)
+    std::vector<get_sensor_state_field>& stateField,
+    const stateSensorCacheMaps& sensorCache)
 {
     using namespace pldm::responder::pdr;
     using namespace pldm::utils;
@@ -146,22 +147,44 @@
         const auto& [dbusMappings, dbusValMaps] = handler.getDbusObjMaps(
             sensorId, pldm::responder::pdr_utils::TypeId::PLDM_SENSOR_ID);
 
+        pldm::responder::pdr_utils::EventStates sensorCacheforSensor{};
+        if (sensorCache.contains(sensorId))
+        {
+            sensorCacheforSensor = sensorCache.at(sensorId);
+        }
         stateField.clear();
-        for (size_t i = 0; i < sensorRearmCnt; i++)
+        for (std::size_t i{0}; i < sensorRearmCnt; i++)
         {
             auto& dbusMapping = dbusMappings[i];
 
             uint8_t sensorEvent = getStateSensorEventState<DBusInterface>(
                 dBusIntf, dbusValMaps[i], dbusMapping);
 
+            uint8_t previousState = PLDM_SENSOR_UNKNOWN;
+
+            // if sensor cache is empty, then its the first
+            // get_state_sensor_reading on this sensor, set the previous state
+            // as the current state
+
+            if (sensorCacheforSensor.at(i) == PLDM_SENSOR_UNKNOWN)
+            {
+                previousState = sensorEvent;
+                handler.updateSensorCache(sensorId, i, previousState);
+            }
+            else
+            {
+                // sensor cache is not empty, so get the previous state from
+                // the sensor cache
+                previousState = sensorCacheforSensor[i];
+            }
             uint8_t opState = PLDM_SENSOR_ENABLED;
             if (sensorEvent == PLDM_SENSOR_UNKNOWN)
             {
                 opState = PLDM_SENSOR_UNAVAILABLE;
             }
 
-            stateField.push_back({opState, PLDM_SENSOR_NORMAL,
-                                  PLDM_SENSOR_UNKNOWN, sensorEvent});
+            stateField.push_back(
+                {opState, PLDM_SENSOR_NORMAL, previousState, sensorEvent});
         }
     }
     catch (const std::out_of_range& e)
diff --git a/libpldmresponder/test/libpldmresponder_platform_test.cpp b/libpldmresponder/test/libpldmresponder_platform_test.cpp
index c49ebad..9dbe0af 100644
--- a/libpldmresponder/test/libpldmresponder_platform_test.cpp
+++ b/libpldmresponder/test/libpldmresponder_platform_test.cpp
@@ -1,5 +1,6 @@
 #include "common/test/mocked_utils.hpp"
 #include "common/utils.hpp"
+#include "host-bmc/dbus_to_event_handler.hpp"
 #include "libpldmresponder/event_parser.hpp"
 #include "libpldmresponder/pdr.hpp"
 #include "libpldmresponder/pdr_utils.hpp"
@@ -757,15 +758,17 @@
                                        StrEq("xyz.openbmc_project.Foo.Bar")))
         .WillOnce(Return(
             PropertyValue(std::string("xyz.openbmc_project.Foo.Bar.V0"))));
-
+    EventStates cache = {PLDM_SENSOR_NORMAL};
+    pldm::stateSensorCacheMaps sensorCache;
+    sensorCache.emplace(0x1, cache);
     auto rc = platform_state_sensor::getStateSensorReadingsHandler<
         MockdBusHandler, Handler>(handlerObj, handler, 0x1, sensorRearmCnt,
-                                  compSensorCnt, stateField);
+                                  compSensorCnt, stateField, sensorCache);
     ASSERT_EQ(rc, 0);
     ASSERT_EQ(compSensorCnt, 1);
     ASSERT_EQ(stateField[0].sensor_op_state, PLDM_SENSOR_UNAVAILABLE);
     ASSERT_EQ(stateField[0].present_state, PLDM_SENSOR_NORMAL);
-    ASSERT_EQ(stateField[0].previous_state, PLDM_SENSOR_UNKNOWN);
+    ASSERT_EQ(stateField[0].previous_state, PLDM_SENSOR_NORMAL);
     ASSERT_EQ(stateField[0].event_state, PLDM_SENSOR_UNKNOWN);
 
     pldm_pdr_destroy(inPDRRepo);
@@ -800,9 +803,12 @@
     uint8_t sensorRearmCnt = 3;
 
     MockdBusHandler handlerObj;
+    EventStates cache = {PLDM_SENSOR_NORMAL};
+    pldm::stateSensorCacheMaps sensorCache;
+    sensorCache.emplace(0x1, cache);
     auto rc = platform_state_sensor::getStateSensorReadingsHandler<
         MockdBusHandler, Handler>(handlerObj, handler, 0x1, sensorRearmCnt,
-                                  compSensorCnt, stateField);
+                                  compSensorCnt, stateField, sensorCache);
     ASSERT_EQ(rc, PLDM_PLATFORM_REARM_UNAVAILABLE_IN_PRESENT_STATE);
 
     pldm_pdr_destroy(inPDRRepo);