sensor-cache: Read sensor data when there is no cache

When IPMI starts and the sensor has no match callback, the sensor cache
is empty. Read the sensor data in such case and cache it.

Tested: Verify the sensor list works as expected, the first sensor
        list takes longer time, and the following sensor list takes less
        time because of the cache.

Signed-off-by: Lei YU <yulei.sh@bytedance.com>
Change-Id: Icc33185b93cf21462584b3817b7c522e24b70baf
diff --git a/sensordatahandler.hpp b/sensordatahandler.hpp
index 2704146..7ecfee8 100644
--- a/sensordatahandler.hpp
+++ b/sensordatahandler.hpp
@@ -13,7 +13,13 @@
 #include <sdbusplus/message/types.hpp>
 
 #ifdef FEATURE_SENSORS_CACHE
+
 extern ipmi::sensor::SensorCacheMap sensorCacheMap;
+
+// The signal's message type is 0x04 from DBus spec:
+// https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages
+static constexpr auto msgTypeSignal = 0x04;
+
 #endif
 
 namespace ipmi
@@ -397,47 +403,53 @@
 std::optional<GetSensorResponse> readingData(uint8_t id, const Info& sensorInfo,
                                              sdbusplus::message::message& msg)
 {
-    std::string interfaceName;
     std::map<std::string, ipmi::Value> properties;
-    msg.read(interfaceName);
+    auto type = msg.get_type();
+    if (type == msgTypeSignal)
+    {
+        // This is signal callback
+        std::string interfaceName;
+        msg.read(interfaceName);
 
-    if (interfaceName ==
-        "xyz.openbmc_project.State.Decorator.OperationalStatus")
-    {
-        msg.read(properties);
-        auto val = properties.find("Functional");
-        if (val != properties.end())
+        if (interfaceName ==
+            "xyz.openbmc_project.State.Decorator.OperationalStatus")
         {
-            sensorCacheMap[id]->functional = std::get<bool>(val->second);
+            msg.read(properties);
+            auto val = properties.find("Functional");
+            if (val != properties.end())
+            {
+                sensorCacheMap[id]->functional = std::get<bool>(val->second);
+            }
+            return {};
         }
-        return {};
-    }
-    if (interfaceName == "xyz.openbmc_project.State.Decorator.Availability")
-    {
-        msg.read(properties);
-        auto val = properties.find("Available");
-        if (val != properties.end())
+        if (interfaceName == "xyz.openbmc_project.State.Decorator.Availability")
         {
-            sensorCacheMap[id]->available = std::get<bool>(val->second);
+            msg.read(properties);
+            auto val = properties.find("Available");
+            if (val != properties.end())
+            {
+                sensorCacheMap[id]->available = std::get<bool>(val->second);
+            }
+            return {};
         }
-        return {};
-    }
 
-    if (interfaceName != sensorInfo.sensorInterface)
-    {
-        // Not the interface we need
-        return {};
-    }
+        if (interfaceName != sensorInfo.sensorInterface)
+        {
+            // Not the interface we need
+            return {};
+        }
 
 #ifdef UPDATE_FUNCTIONAL_ON_FAIL
-    if (sensorCacheMap[id])
-    {
-        if (!sensorCacheMap[id]->functional)
+        if (sensorCacheMap[id])
         {
-            throw SensorFunctionalError();
+            if (!sensorCacheMap[id]->functional)
+            {
+                throw SensorFunctionalError();
+            }
         }
-    }
 #endif
+    }
+    // Now the message only contains the properties.
 
     GetSensorResponse response{};
 
diff --git a/sensorhandler.cpp b/sensorhandler.cpp
index 7e0c7dc..b82e74b 100644
--- a/sensorhandler.cpp
+++ b/sensorhandler.cpp
@@ -496,18 +496,39 @@
         const auto& sensorData = sensorCacheMap[sensorNum];
         if (!sensorData.has_value())
         {
-            // Intitilizing with default values
-            constexpr uint8_t senReading = 0;
-            constexpr uint5_t reserved{0};
-            constexpr bool readState = true;
-            constexpr bool senScanState = false;
-            constexpr bool allEventMessageState = false;
-            constexpr uint8_t assertionStatesLsb = 0;
-            constexpr uint8_t assertionStatesMsb = 0;
+            // No cached value, try read it
+            sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+            const auto& sensorInfo = iter->second;
+            auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
+                                            sensorInfo.sensorPath);
+            try
+            {
+                auto method = bus.new_method_call(
+                    service.c_str(), sensorInfo.sensorPath.c_str(),
+                    "org.freedesktop.DBus.Properties", "GetAll");
+                method.append(
+                    sensorInfo.propertyInterfaces.begin()->first.c_str());
+                auto reply = bus.call(method);
+                sensorInfo.getFunc(sensorNum, sensorInfo, reply);
+            }
+            catch (const std::exception& e)
+            {
+                fprintf(stderr, "Failed to get sensor %s\n",
+                        sensorInfo.sensorPath.c_str());
+                // Intitilizing with default values
+                constexpr uint8_t senReading = 0;
+                constexpr uint5_t reserved{0};
+                constexpr bool readState = true;
+                constexpr bool senScanState = false;
+                constexpr bool allEventMessageState = false;
+                constexpr uint8_t assertionStatesLsb = 0;
+                constexpr uint8_t assertionStatesMsb = 0;
 
-            return ipmi::responseSuccess(
-                senReading, reserved, readState, senScanState,
-                allEventMessageState, assertionStatesLsb, assertionStatesMsb);
+                return ipmi::responseSuccess(senReading, reserved, readState,
+                                             senScanState, allEventMessageState,
+                                             assertionStatesLsb,
+                                             assertionStatesMsb);
+            }
         }
         return ipmi::responseSuccess(
             sensorData->response.reading, uint5_t(0),