platform-mc: Added EventManager

Added eventManager to handle sensor event class(00h) which is defined in
table 11 of DSP0248 v1.3.0. In this commit, the eventManager supports to
receive event asynchronously. The commit will also log the Ipmitool SEL
log and Redfish log for PLDM sensor event messages.

Change-Id: I1b337ccae454067841ffbbd8754631216a995542
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Signed-off-by: Gilbert Chen <gilbertc@nvidia.com>
diff --git a/platform-mc/test/event_manager_test.cpp b/platform-mc/test/event_manager_test.cpp
new file mode 100644
index 0000000..01abf42
--- /dev/null
+++ b/platform-mc/test/event_manager_test.cpp
@@ -0,0 +1,390 @@
+#include "libpldm/base.h"
+#include "libpldm/entity.h"
+#include "libpldm/platform.h"
+
+#include "common/instance_id.hpp"
+#include "common/types.hpp"
+#include "mock_event_manager.hpp"
+#include "mock_terminus_manager.hpp"
+#include "platform-mc/platform_manager.hpp"
+#include "platform-mc/terminus_manager.hpp"
+#include "test/test_instance_id.hpp"
+
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::Return;
+
+class EventManagerTest : public testing::Test
+{
+  protected:
+    EventManagerTest() :
+        bus(pldm::utils::DBusHandler::getBus()),
+        event(sdeventplus::Event::get_default()), instanceIdDb(),
+        reqHandler(pldmTransport, event, instanceIdDb, false,
+                   std::chrono::seconds(1), 2, std::chrono::milliseconds(100)),
+        terminusManager(event, reqHandler, instanceIdDb, termini, nullptr),
+        eventManager(terminusManager, termini),
+        platformManager(terminusManager, termini)
+    {}
+
+    PldmTransport* pldmTransport = nullptr;
+    sdbusplus::bus::bus& bus;
+    sdeventplus::Event event;
+    TestInstanceIdDb instanceIdDb;
+    pldm::requester::Handler<pldm::requester::Request> reqHandler;
+    pldm::platform_mc::MockTerminusManager terminusManager;
+    pldm::platform_mc::MockEventManager eventManager;
+    pldm::platform_mc::PlatformManager platformManager;
+    pldm::platform_mc::TerminiMapper termini{};
+};
+
+TEST_F(EventManagerTest, processNumericSensorEventTest)
+{
+#define SENSOR_READING 50
+#define WARNING_HIGH 45
+    pldm_tid_t tid = 1;
+    termini[tid] = std::make_shared<pldm::platform_mc::Terminus>(
+        tid, 1 << PLDM_BASE | 1 << PLDM_PLATFORM);
+    std::vector<uint8_t> pdr1{
+        0x1,
+        0x0,
+        0x0,
+        0x0,                         // record handle
+        0x1,                         // PDRHeaderVersion
+        PLDM_NUMERIC_SENSOR_PDR,     // PDRType
+        0x0,
+        0x0,                         // recordChangeNumber
+        PLDM_PDR_NUMERIC_SENSOR_PDR_MIN_LENGTH,
+        0,                           // dataLength
+        0,
+        0,                           // PLDMTerminusHandle
+        0x1,
+        0x0,                         // sensorID=1
+        PLDM_ENTITY_POWER_SUPPLY,
+        0,                           // entityType=Power Supply(120)
+        1,
+        0,                           // entityInstanceNumber
+        1,
+        0,                           // containerID=1
+        PLDM_NO_INIT,                // sensorInit
+        false,                       // sensorAuxiliaryNamesPDR
+        PLDM_SENSOR_UNIT_DEGRESS_C,  // baseUint(2)=degrees C
+        0,                           // unitModifier = 0
+        0,                           // rateUnit
+        0,                           // baseOEMUnitHandle
+        0,                           // auxUnit
+        0,                           // auxUnitModifier
+        0,                           // auxRateUnit
+        0,                           // rel
+        0,                           // auxOEMUnitHandle
+        true,                        // isLinear
+        PLDM_SENSOR_DATA_SIZE_UINT8, // sensorDataSize
+        0,
+        0,
+        0x80,
+        0x3f, // resolution=1.0
+        0,
+        0,
+        0,
+        0,    // offset=0
+        0,
+        0,    // accuracy
+        0,    // plusTolerance
+        0,    // minusTolerance
+        2,    // hysteresis = 2
+        0x1b, // supportedThresholds
+        0,    // thresholdAndHysteresisVolatility
+        0,
+        0,
+        0x80,
+        0x3f, // stateTransistionInterval=1.0
+        0,
+        0,
+        0x80,
+        0x3f,                          // updateInverval=1.0
+        255,                           // maxReadable
+        0,                             // minReadable
+        PLDM_RANGE_FIELD_FORMAT_UINT8, // rangeFieldFormat
+        0x18,                          // rangeFieldsupport
+        0,                             // nominalValue
+        0,                             // normalMax
+        0,                             // normalMin
+        WARNING_HIGH,                  // warningHigh
+        20,                            // warningLow
+        60,                            // criticalHigh
+        10,                            // criticalLow
+        0,                             // fatalHigh
+        0                              // fatalLow
+    };
+
+    std::vector<uint8_t> pdr2{
+        0x1, 0x0, 0x0,
+        0x0,                             // record handle
+        0x1,                             // PDRHeaderVersion
+        PLDM_ENTITY_AUXILIARY_NAMES_PDR, // PDRType
+        0x1,
+        0x0,                             // recordChangeNumber
+        0x11,
+        0,                               // dataLength
+        /* Entity Auxiliary Names PDR Data*/
+        3,
+        0x80, // entityType system software
+        0x1,
+        0x0,  // Entity instance number =1
+        0,
+        0,    // Overal system
+        0,    // shared Name Count one name only
+        01,   // nameStringCount
+        0x65, 0x6e, 0x00,
+        0x00, // Language Tag "en"
+        0x53, 0x00, 0x30, 0x00,
+        0x00  // Entity Name "S0"
+    };
+
+    // add dummy numeric sensor
+    termini[tid]->pdrs.emplace_back(pdr1);
+    termini[tid]->pdrs.emplace_back(pdr2);
+    termini[tid]->parseTerminusPDRs();
+    EXPECT_EQ(1, termini[tid]->numericSensors.size());
+
+    uint8_t platformEventStatus = 0;
+
+    std::vector<uint8_t> eventData{
+        0x1,
+        0x0, // sensor id
+        PLDM_NUMERIC_SENSOR_STATE,
+        PLDM_SENSOR_UPPERWARNING,
+        PLDM_SENSOR_NORMAL,
+        PLDM_SENSOR_DATA_SIZE_UINT8,
+        SENSOR_READING};
+    auto rc = eventManager.handlePlatformEvent(
+        tid, 0x00, PLDM_SENSOR_EVENT, eventData.data(), eventData.size());
+    EXPECT_EQ(PLDM_SUCCESS, rc);
+    EXPECT_EQ(PLDM_EVENT_NO_LOGGING, platformEventStatus);
+}
+
+TEST_F(EventManagerTest, SetEventReceiverTest)
+{
+    // Add terminus
+    auto mappedTid = terminusManager.mapTid(pldm::MctpInfo(10, "", "", 1));
+    auto tid = mappedTid.value();
+    termini[tid] = std::make_shared<pldm::platform_mc::Terminus>(
+        tid, 1 << PLDM_BASE | 1 << PLDM_PLATFORM);
+    auto terminus = termini[tid];
+
+    /* Set supported command by terminus */
+    auto size = PLDM_MAX_TYPES * (PLDM_MAX_CMDS_PER_TYPE / 8);
+    std::vector<uint8_t> pldmCmds(size);
+    uint8_t type = PLDM_PLATFORM;
+    uint8_t cmd = PLDM_GET_PDR_REPOSITORY_INFO;
+    auto idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + (cmd / 8);
+    pldmCmds[idx] = pldmCmds[idx] | (1 << (cmd % 8));
+    cmd = PLDM_GET_PDR;
+    idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + (cmd / 8);
+    pldmCmds[idx] = pldmCmds[idx] | (1 << (cmd % 8));
+    cmd = PLDM_EVENT_MESSAGE_SUPPORTED;
+    idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + (cmd / 8);
+    pldmCmds[idx] = pldmCmds[idx] | (1 << (cmd % 8));
+    cmd = PLDM_EVENT_MESSAGE_BUFFER_SIZE;
+    idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + (cmd / 8);
+    pldmCmds[idx] = pldmCmds[idx] | (1 << (cmd % 8));
+    cmd = PLDM_SET_EVENT_RECEIVER;
+    idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + (cmd / 8);
+    pldmCmds[idx] = pldmCmds[idx] | (1 << (cmd % 8));
+    termini[tid]->setSupportedCommands(pldmCmds);
+
+    EXPECT_EQ(true, termini[tid]->doesSupportCommand(PLDM_PLATFORM,
+                                                     PLDM_SET_EVENT_RECEIVER));
+    EXPECT_EQ(true, termini[tid]->doesSupportCommand(
+                        PLDM_PLATFORM, PLDM_EVENT_MESSAGE_BUFFER_SIZE));
+    EXPECT_EQ(true, termini[tid]->doesSupportCommand(
+                        PLDM_PLATFORM, PLDM_EVENT_MESSAGE_SUPPORTED));
+    EXPECT_EQ(true,
+              termini[tid]->doesSupportCommand(PLDM_PLATFORM, PLDM_GET_PDR));
+    EXPECT_EQ(true, termini[tid]->doesSupportCommand(
+                        PLDM_PLATFORM, PLDM_GET_PDR_REPOSITORY_INFO));
+
+    // queue getPDRRepositoryInfo response
+    const size_t getPDRRepositoryInfoLen =
+        PLDM_GET_PDR_REPOSITORY_INFO_RESP_BYTES;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + getPDRRepositoryInfoLen>
+        getPDRRepositoryInfoResp{
+            0x0, 0x02, 0x50, PLDM_SUCCESS,
+            0x0,                                     // repositoryState
+            0x0, 0x0,  0x0,  0x0,          0x0, 0x0, 0x0,
+            0x0, 0x0,  0x0,  0x0,          0x0, 0x0, // updateTime
+            0x0, 0x0,  0x0,  0x0,          0x0, 0x0, 0x0,
+            0x0, 0x0,  0x0,  0x0,          0x0, 0x0, // OEMUpdateTime
+            2,   0x0,  0x0,  0x0,                    // recordCount
+            0x0, 0x1,  0x0,  0x0,                    // repositorySize
+            59,  0x0,  0x0,  0x0,                    // largestRecordSize
+            0x0 // dataTransferHandleTimeout
+        };
+    auto rc = terminusManager.enqueueResponse(
+        reinterpret_cast<pldm_msg*>(getPDRRepositoryInfoResp.data()),
+        sizeof(getPDRRepositoryInfoResp));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    // queue getPDR responses
+    const size_t getPdrRespLen = 81;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + getPdrRespLen> getPdrResp{
+        0x0, 0x02, 0x51, PLDM_SUCCESS, 0x1, 0x0, 0x0, 0x0, // nextRecordHandle
+        0x0, 0x0, 0x0, 0x0, // nextDataTransferHandle
+        0x5,                // transferFlag
+        69, 0x0,            // responseCount
+        // numeric Sensor PDR
+        0x0, 0x0, 0x0,
+        0x0,                     // record handle
+        0x1,                     // PDRHeaderVersion
+        PLDM_NUMERIC_SENSOR_PDR, // PDRType
+        0x0,
+        0x0,                     // recordChangeNumber
+        PLDM_PDR_NUMERIC_SENSOR_PDR_FIXED_LENGTH +
+            PLDM_PDR_NUMERIC_SENSOR_PDR_VARIED_SENSOR_DATA_SIZE_MIN_LENGTH +
+            PLDM_PDR_NUMERIC_SENSOR_PDR_VARIED_RANGE_FIELD_MIN_LENGTH,
+        0,                             // dataLength
+        0,
+        0,                             // PLDMTerminusHandle
+        0x1,
+        0x0,                           // sensorID=1
+        120,
+        0,                             // entityType=Power Supply(120)
+        1,
+        0,                             // entityInstanceNumber
+        0x1,
+        0x0,                           // containerID=1
+        PLDM_NO_INIT,                  // sensorInit
+        false,                         // sensorAuxiliaryNamesPDR
+        PLDM_SENSOR_UNIT_DEGRESS_C,    // baseUint(2)=degrees C
+        1,                             // unitModifier = 1
+        0,                             // rateUnit
+        0,                             // baseOEMUnitHandle
+        0,                             // auxUnit
+        0,                             // auxUnitModifier
+        0,                             // auxRateUnit
+        0,                             // rel
+        0,                             // auxOEMUnitHandle
+        true,                          // isLinear
+        PLDM_SENSOR_DATA_SIZE_UINT8,   // sensorDataSize
+        0, 0, 0xc0,
+        0x3f,                          // resolution=1.5
+        0, 0, 0x80,
+        0x3f,                          // offset=1.0
+        0,
+        0,                             // accuracy
+        0,                             // plusTolerance
+        0,                             // minusTolerance
+        2,                             // hysteresis
+        0,                             // supportedThresholds
+        0,                             // thresholdAndHysteresisVolatility
+        0, 0, 0x80,
+        0x3f,                          // stateTransistionInterval=1.0
+        0, 0, 0x80,
+        0x3f,                          // updateInverval=1.0
+        255,                           // maxReadable
+        0,                             // minReadable
+        PLDM_RANGE_FIELD_FORMAT_UINT8, // rangeFieldFormat
+        0,                             // rangeFieldsupport
+        0,                             // nominalValue
+        0,                             // normalMax
+        0,                             // normalMin
+        0,                             // warningHigh
+        0,                             // warningLow
+        0,                             // criticalHigh
+        0,                             // criticalLow
+        0,                             // fatalHigh
+        0                              // fatalLow
+    };
+    rc = terminusManager.enqueueResponse(
+        reinterpret_cast<pldm_msg*>(getPdrResp.data()), sizeof(getPdrResp));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    const size_t getPdrAuxNameRespLen = 39;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + getPdrAuxNameRespLen>
+        getPdrAuxNameResp{
+            0x0, 0x02, 0x51, PLDM_SUCCESS, 0x0, 0x0, 0x0,
+            0x0,                // nextRecordHandle
+            0x0, 0x0, 0x0, 0x0, // nextDataTransferHandle
+            0x5,                // transferFlag
+            0x1b, 0x0,          // responseCount
+            // Common PDR Header
+            0x1, 0x0, 0x0,
+            0x0,                             // record handle
+            0x1,                             // PDRHeaderVersion
+            PLDM_ENTITY_AUXILIARY_NAMES_PDR, // PDRType
+            0x1,
+            0x0,                             // recordChangeNumber
+            0x11,
+            0,                               // dataLength
+            /* Entity Auxiliary Names PDR Data*/
+            3,
+            0x80, // entityType system software
+            0x1,
+            0x0,  // Entity instance number =1
+            0,
+            0,    // Overal system
+            0,    // shared Name Count one name only
+            01,   // nameStringCount
+            0x65, 0x6e, 0x00,
+            0x00, // Language Tag "en"
+            0x53, 0x00, 0x30, 0x00,
+            0x00  // Entity Name "S0"
+        };
+    rc = terminusManager.enqueueResponse(
+        reinterpret_cast<pldm_msg*>(getPdrAuxNameResp.data()),
+        sizeof(getPdrAuxNameResp));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    // queue eventMessageBufferSize response(bufferSize=32)
+    const size_t eventMessageBufferSizeRespLen = 3;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + eventMessageBufferSizeRespLen>
+        eventMessageBufferSizeResp{0x0, 0x02, 0x0d, PLDM_SUCCESS, 32, 0};
+    rc = terminusManager.enqueueResponse(
+        reinterpret_cast<pldm_msg*>(eventMessageBufferSizeResp.data()),
+        sizeof(eventMessageBufferSizeResp));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    // queue eventMessageSupported response
+    const size_t eventMessageSupportedLen = 7;
+    PLDM_GET_PDR_REPOSITORY_INFO_RESP_BYTES;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + eventMessageSupportedLen>
+        eventMessageSupportedResp{0x0,  0x02, 0x0c, PLDM_SUCCESS,
+                                  0x0,  // synchronyConfiguration
+                                  0x06, // synchronyConfigurationSupported
+                                  3,    // numberEventClassReturned
+                                  0x0,  0x5,  0xfa};
+    rc = terminusManager.enqueueResponse(
+        reinterpret_cast<pldm_msg*>(eventMessageSupportedResp.data()),
+        sizeof(eventMessageSupportedResp));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    // queue SetEventReceiver response
+    const size_t SetEventReceiverLen = 1;
+    PLDM_GET_PDR_REPOSITORY_INFO_RESP_BYTES;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + SetEventReceiverLen>
+        SetEventReceiverResp{0x0, 0x02, 0x04, PLDM_SUCCESS};
+    rc = terminusManager.enqueueResponse(
+        reinterpret_cast<pldm_msg*>(SetEventReceiverResp.data()),
+        sizeof(SetEventReceiverResp));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    // should finish immediately
+    stdexec::sync_wait(platformManager.initTerminus());
+    EXPECT_EQ(true, terminus->initialized);
+    EXPECT_EQ(32, terminus->maxBufferSize);
+    EXPECT_EQ(0x06, terminus->synchronyConfigurationSupported.byte);
+    EXPECT_EQ(2, terminus->pdrs.size());
+    EXPECT_EQ(1, terminus->numericSensors.size());
+}
+
+TEST_F(EventManagerTest, updateAvailableState)
+{
+    pldm_tid_t tid = 1;
+    eventManager.updateAvailableState(tid, true);
+    EXPECT_EQ(true, eventManager.getAvailableState(tid));
+    eventManager.updateAvailableState(tid, false);
+    EXPECT_EQ(false, eventManager.getAvailableState(tid));
+    eventManager.updateAvailableState(2, false);
+    EXPECT_EQ(false, eventManager.getAvailableState(tid));
+}
diff --git a/platform-mc/test/meson.build b/platform-mc/test/meson.build
index ab7b20a..0e8bc87 100644
--- a/platform-mc/test/meson.build
+++ b/platform-mc/test/meson.build
@@ -6,6 +6,7 @@
         '../manager.cpp',
         '../sensor_manager.cpp',
         '../numeric_sensor.cpp',
+        '../event_manager.cpp',
         '../../requester/mctp_endpoint_discovery.cpp',
     ],
     include_directories: ['../../requester', '../../pldmd'],
@@ -17,6 +18,7 @@
     'platform_manager_test',
     'sensor_manager_test',
     'numeric_sensor_test',
+    'event_manager_test',
 ]
 
 foreach t : tests
diff --git a/platform-mc/test/mock_event_manager.hpp b/platform-mc/test/mock_event_manager.hpp
new file mode 100644
index 0000000..116e7f1
--- /dev/null
+++ b/platform-mc/test/mock_event_manager.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "platform-mc/event_manager.hpp"
+
+#include <gmock/gmock.h>
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+class MockEventManager : public EventManager
+{
+  public:
+    MockEventManager(TerminusManager& terminusManager, TerminiMapper& termini) :
+        EventManager(terminusManager, termini) {};
+};
+
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/test/mock_sensor_manager.hpp b/platform-mc/test/mock_sensor_manager.hpp
index a52e4ea..fcb8102 100644
--- a/platform-mc/test/mock_sensor_manager.hpp
+++ b/platform-mc/test/mock_sensor_manager.hpp
@@ -13,9 +13,9 @@
 {
   public:
     MockSensorManager(sdeventplus::Event& event,
-                      TerminusManager& terminusManager,
-                      TerminiMapper& termini) :
-        SensorManager(event, terminusManager, termini) {};
+                      TerminusManager& terminusManager, TerminiMapper& termini,
+                      Manager* manager) :
+        SensorManager(event, terminusManager, termini, manager) {};
 
     MOCK_METHOD(void, doSensorPolling, (pldm_tid_t tid), (override));
 };
diff --git a/platform-mc/test/sensor_manager_test.cpp b/platform-mc/test/sensor_manager_test.cpp
index 59a120b..4ec9f5b 100644
--- a/platform-mc/test/sensor_manager_test.cpp
+++ b/platform-mc/test/sensor_manager_test.cpp
@@ -19,7 +19,7 @@
         reqHandler(pldmTransport, event, instanceIdDb, false),
         terminusManager(event, reqHandler, instanceIdDb, termini, nullptr,
                         pldm::BmcMctpEid),
-        sensorManager(event, terminusManager, termini)
+        sensorManager(event, terminusManager, termini, nullptr)
     {}
 
     void runEventLoopForSeconds(uint64_t sec)