platform-mc: Add sensor manager

Added sensor_manager class. The sensor_manager class manages the timing
of sensor polling.

tested: Verified on ast2600 EVB which is connected to a PLDM device
over I2C. bmcweb can display the state of numeric sensor.

Signed-off-by: Gilbert Chen <gilbert.chen@arm.com>
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I4257f823ea26d7fdb322cc82d847e94db056258c
diff --git a/platform-mc/test/meson.build b/platform-mc/test/meson.build
index 6b95f90..64177cc 100644
--- a/platform-mc/test/meson.build
+++ b/platform-mc/test/meson.build
@@ -4,6 +4,7 @@
             '../terminus.cpp',
             '../platform_manager.cpp',
             '../manager.cpp',
+            '../sensor_manager.cpp',
             '../numeric_sensor.cpp',
             '../../requester/mctp_endpoint_discovery.cpp'],
             include_directories: ['../../requester', '../../pldmd'])
@@ -12,6 +13,8 @@
   'terminus_manager_test',
   'terminus_test',
   'platform_manager_test',
+  'sensor_manager_test',
+  'numeric_sensor_test',
 ]
 
 foreach t : tests
diff --git a/platform-mc/test/mock_sensor_manager.hpp b/platform-mc/test/mock_sensor_manager.hpp
new file mode 100644
index 0000000..a52e4ea
--- /dev/null
+++ b/platform-mc/test/mock_sensor_manager.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "platform-mc/sensor_manager.hpp"
+
+#include <gmock/gmock.h>
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+class MockSensorManager : public SensorManager
+{
+  public:
+    MockSensorManager(sdeventplus::Event& event,
+                      TerminusManager& terminusManager,
+                      TerminiMapper& termini) :
+        SensorManager(event, terminusManager, termini) {};
+
+    MOCK_METHOD(void, doSensorPolling, (pldm_tid_t tid), (override));
+};
+
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/test/numeric_sensor_test.cpp b/platform-mc/test/numeric_sensor_test.cpp
new file mode 100644
index 0000000..8b59401
--- /dev/null
+++ b/platform-mc/test/numeric_sensor_test.cpp
@@ -0,0 +1,272 @@
+
+#include "libpldm/entity.h"
+#include "libpldm/platform.h"
+
+#include "platform-mc/numeric_sensor.hpp"
+#include "platform-mc/terminus.hpp"
+
+#include <gtest/gtest.h>
+
+TEST(NumericSensor, conversionFormula)
+{
+    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_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
+        PLDM_ENTITY_POWER_SUPPLY,
+        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_RANGE_FIELD_FORMAT_SINT8, // 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
+    };
+
+    auto numericSensorPdr = std::make_shared<pldm_numeric_sensor_value_pdr>();
+    std::printf("pdr size=%ld\n", pdr1.size());
+    auto rc = decode_numeric_sensor_pdr_data(pdr1.data(), pdr1.size(),
+                                             numericSensorPdr.get());
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    std::string sensorName{"test1"};
+    std::string inventoryPath{
+        "/xyz/openbmc_project/inventroy/Item/Board/PLDM_device_1"};
+    pldm::platform_mc::NumericSensor sensor(0x01, true, numericSensorPdr,
+                                            sensorName, inventoryPath);
+    double reading = 40.0;
+    double convertedValue = 0;
+    convertedValue = sensor.conversionFormula(reading);
+    convertedValue = sensor.unitModifier(convertedValue);
+
+    // (40*1.5 + 1.0 ) * 10^1 = 610
+    EXPECT_EQ(610, convertedValue);
+}
+
+TEST(NumericSensor, checkThreshold)
+{
+    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_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
+        PLDM_ENTITY_POWER_SUPPLY,
+        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_RANGE_FIELD_FORMAT_SINT8, // 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
+    };
+
+    auto numericSensorPdr = std::make_shared<pldm_numeric_sensor_value_pdr>();
+    auto rc = decode_numeric_sensor_pdr_data(pdr1.data(), pdr1.size(),
+                                             numericSensorPdr.get());
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    std::string sensorName{"test1"};
+    std::string inventoryPath{
+        "/xyz/openbmc_project/inventroy/Item/Board/PLDM_device_1"};
+    pldm::platform_mc::NumericSensor sensor(0x01, true, numericSensorPdr,
+                                            sensorName, inventoryPath);
+
+    bool highAlarm = false;
+    bool lowAlarm = false;
+    double highThreshold = 40;
+    double lowThreshold = 30;
+    double hysteresis = 2;
+
+    // reading     35->40->45->38->35->30->25->32->35
+    // highAlarm    F->T ->T ->T ->F ->F ->F -> F-> F
+    // lowAlarm     F->F ->F ->F ->F ->T ->T -> T ->F
+    double reading = 35;
+    highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+                                      hysteresis);
+    EXPECT_EQ(false, highAlarm);
+    lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+                                     hysteresis);
+    EXPECT_EQ(false, lowAlarm);
+
+    reading = 40;
+    highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+                                      hysteresis);
+    EXPECT_EQ(true, highAlarm);
+    lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+                                     hysteresis);
+    EXPECT_EQ(false, lowAlarm);
+
+    reading = 45;
+    highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+                                      hysteresis);
+    EXPECT_EQ(true, highAlarm);
+    lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+                                     hysteresis);
+    EXPECT_EQ(false, lowAlarm);
+
+    reading = 38;
+    highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+                                      hysteresis);
+    EXPECT_EQ(true, highAlarm);
+    lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+                                     hysteresis);
+    EXPECT_EQ(false, lowAlarm);
+
+    reading = 35;
+    highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+                                      hysteresis);
+    EXPECT_EQ(false, highAlarm);
+    lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+                                     hysteresis);
+    EXPECT_EQ(false, lowAlarm);
+
+    reading = 30;
+    highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+                                      hysteresis);
+    EXPECT_EQ(false, highAlarm);
+    lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+                                     hysteresis);
+    EXPECT_EQ(true, lowAlarm);
+
+    reading = 25;
+    highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+                                      hysteresis);
+    EXPECT_EQ(false, highAlarm);
+    lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+                                     hysteresis);
+    EXPECT_EQ(true, lowAlarm);
+
+    reading = 32;
+    highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+                                      hysteresis);
+    EXPECT_EQ(false, highAlarm);
+    lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+                                     hysteresis);
+    EXPECT_EQ(true, lowAlarm);
+
+    reading = 35;
+    highAlarm = sensor.checkThreshold(highAlarm, true, reading, highThreshold,
+                                      hysteresis);
+    EXPECT_EQ(false, highAlarm);
+    lowAlarm = sensor.checkThreshold(lowAlarm, false, reading, lowThreshold,
+                                     hysteresis);
+    EXPECT_EQ(false, lowAlarm);
+}
diff --git a/platform-mc/test/platform_manager_test.cpp b/platform-mc/test/platform_manager_test.cpp
index 8afad35..73baa1a 100644
--- a/platform-mc/test/platform_manager_test.cpp
+++ b/platform-mc/test/platform_manager_test.cpp
@@ -63,7 +63,7 @@
             0x0, 0x0,  0x0,  0x0,          0x0, 0x0, // updateTime
             0x0, 0x0,  0x0,  0x0,          0x0, 0x0, 0x0,
             0x0, 0x0,  0x0,  0x0,          0x0, 0x0, // OEMUpdateTime
-            1,   0x0,  0x0,  0x0,                    // recordCount
+            2,   0x0,  0x0,  0x0,                    // recordCount
             0x0, 0x1,  0x0,  0x0,                    // repositorySize
             59,  0x0,  0x0,  0x0,                    // largestRecordSize
             0x0 // dataTransferHandleTimeout
@@ -76,12 +76,12 @@
     // queue getPDR responses
     const size_t getPdrRespLen = 81;
     std::array<uint8_t, sizeof(pldm_msg_hdr) + getPdrRespLen> getPdrResp{
-        0x0, 0x02, 0x51, PLDM_SUCCESS, 0x0, 0x0, 0x0, 0x0, // nextRecordHandle
+        0x0, 0x02, 0x51, PLDM_SUCCESS, 0x1, 0x0, 0x0, 0x0, // nextRecordHandle
         0x0, 0x0, 0x0, 0x0, // nextDataTransferHandle
         0x5,                // transferFlag
         69, 0x0,            // responseCount
         // numeric Sensor PDR
-        0x1, 0x0, 0x0,
+        0x0, 0x0, 0x0,
         0x0,                     // record handle
         0x1,                     // PDRHeaderVersion
         PLDM_NUMERIC_SENSOR_PDR, // PDRType
@@ -147,9 +147,47 @@
         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 = mockTerminusManager.enqueueResponse(
+        reinterpret_cast<pldm_msg*>(getPdrAuxNameResp.data()),
+        sizeof(getPdrAuxNameResp));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
     stdexec::sync_wait(platformManager.initTerminus());
     EXPECT_EQ(true, terminus->initialized);
-    EXPECT_EQ(1, terminus->pdrs.size());
+    EXPECT_EQ(2, terminus->pdrs.size());
+    EXPECT_EQ(1, terminus->numericSensors.size());
+    EXPECT_EQ("S0", terminus->getTerminusName());
 }
 
 TEST_F(PlatformManagerTest, parseTerminusNameTest)
@@ -440,6 +478,7 @@
     stdexec::sync_wait(platformManager.initTerminus());
     EXPECT_EQ(true, terminus->initialized);
     EXPECT_EQ(0, terminus->pdrs.size());
+    EXPECT_EQ(0, terminus->numericSensors.size());
 }
 
 TEST_F(PlatformManagerTest, negativeInitTerminusTest2)
@@ -471,4 +510,5 @@
     stdexec::sync_wait(platformManager.initTerminus());
     EXPECT_EQ(true, terminus->initialized);
     EXPECT_EQ(0, terminus->pdrs.size());
+    EXPECT_EQ(0, terminus->numericSensors.size());
 }
diff --git a/platform-mc/test/sensor_manager_test.cpp b/platform-mc/test/sensor_manager_test.cpp
new file mode 100644
index 0000000..03cb7fd
--- /dev/null
+++ b/platform-mc/test/sensor_manager_test.cpp
@@ -0,0 +1,172 @@
+#include "common/instance_id.hpp"
+#include "common/types.hpp"
+#include "mock_sensor_manager.hpp"
+#include "platform-mc/terminus_manager.hpp"
+#include "test/test_instance_id.hpp"
+
+#include <sdeventplus/event.hpp>
+
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::Between;
+using ::testing::Return;
+
+class SensorManagerTest : public testing::Test
+{
+  protected:
+    SensorManagerTest() :
+        bus(pldm::utils::DBusHandler::getBus()),
+        event(sdeventplus::Event::get_default()), instanceIdDb(),
+        reqHandler(pldmTransport, event, instanceIdDb, false),
+        terminusManager(event, reqHandler, instanceIdDb, termini, nullptr),
+        sensorManager(event, terminusManager, termini)
+    {}
+
+    void runEventLoopForSeconds(uint64_t sec)
+    {
+        uint64_t t0 = 0;
+        uint64_t t1 = 0;
+        uint64_t usec = sec * 1000000;
+        uint64_t elapsed = 0;
+        sd_event_now(event.get(), CLOCK_MONOTONIC, &t0);
+        do
+        {
+            if (!sd_event_run(event.get(), usec - elapsed))
+            {
+                break;
+            }
+            sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
+            elapsed = t1 - t0;
+        } while (elapsed < usec);
+    }
+
+    PldmTransport* pldmTransport = nullptr;
+    sdbusplus::bus::bus& bus;
+    sdeventplus::Event event;
+    TestInstanceIdDb instanceIdDb;
+    pldm::requester::Handler<pldm::requester::Request> reqHandler;
+    pldm::platform_mc::TerminusManager terminusManager;
+    pldm::platform_mc::MockSensorManager sensorManager;
+    std::map<pldm_tid_t, std::shared_ptr<pldm::platform_mc::Terminus>> termini;
+
+    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_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
+        PLDM_ENTITY_POWER_SUPPLY,
+        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_RANGE_FIELD_FORMAT_SINT8, // 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
+    };
+
+    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"
+    };
+};
+
+TEST_F(SensorManagerTest, sensorPollingTest)
+{
+    uint64_t seconds = 10;
+    uint64_t expectedTimes = (seconds * 1000) / SENSOR_POLLING_TIME;
+
+    pldm_tid_t tid = 1;
+    termini[tid] = std::make_shared<pldm::platform_mc::Terminus>(tid, 0);
+    termini[tid]->pdrs.push_back(pdr1);
+    termini[tid]->pdrs.push_back(pdr2);
+    termini[tid]->parseTerminusPDRs();
+
+    EXPECT_CALL(sensorManager, doSensorPolling(tid))
+        .Times(Between(expectedTimes - 3, expectedTimes + 3))
+        .WillRepeatedly(Return());
+
+    sensorManager.startPolling(tid);
+
+    runEventLoopForSeconds(seconds);
+
+    sensorManager.stopPolling(tid);
+}