Support multiple sensors of same fru_type/label

There are some DDR5 DIMMs that have multiple DRAM temperature sensors,
of which we only care about the hotter one.

As the OCC reports these with the same fru_type and label values, the
code will need to read them both, and then just put the maximum value on
D-Bus.

Support this generically by first looping through all of the sysfs files
and saving the temperature values along with the calculated D-Bus object
path, handling the multiple sensor case by only storing the maximum
value, and then looping through the saved values and putting them on
D-Bus.

Tested:
Found sensors 33 and 34 are the same fru type and label:

temp33_fru_type:2
temp33_label:3489726464
temp33_input:38000

temp34_fru_type:2
temp34_label:3489726464
temp34_input:39000

Before change, the cooler one was on D-Bus:
```
d 38
```

After change, the hotter one was:

```
temp33_input:39000
temp34_input:41000

d 41
```

Change-Id: Ida632b9784209079d9d1bfc6c21918cd65be6a1f
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/occ_manager.cpp b/occ_manager.cpp
index 69d3bc9..700b7af 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -762,6 +762,12 @@
 #ifdef READ_OCC_SENSORS
 void Manager::readTempSensors(const fs::path& path, uint32_t id)
 {
+    // There may be more than one sensor with the same FRU type
+    // and label so make two passes: the first to read the temps
+    // from sysfs, and the second to put them on D-Bus after
+    // resolving any conflicts.
+    std::map<std::string, double> sensorData;
+
     std::regex expr{"temp\\d+_label$"}; // Example: temp5_label
     for (auto& file : fs::directory_iterator(path))
     {
@@ -911,12 +917,13 @@
         // NOTE: if OCC sends back 0xFF kernal sets this fault value to 1.
         if (faultValue != 0)
         {
-            dbus::OccDBusSensors::getOccDBus().setValue(
-                sensorPath, std::numeric_limits<double>::quiet_NaN());
-
-            dbus::OccDBusSensors::getOccDBus().setOperationalStatus(sensorPath,
-                                                                    false);
-
+            // For cases when there are multiple readings per fru type/label,
+            // don't overwrite a good value with an NaN.
+            if (!sensorData.contains(sensorPath))
+            {
+                sensorData[sensorPath] =
+                    std::numeric_limits<double>::quiet_NaN();
+            }
             continue;
         }
 
@@ -948,22 +955,39 @@
             }
         }
 
-        dbus::OccDBusSensors::getOccDBus().setValue(
-            sensorPath, tempValue * std::pow(10, -3));
+        // If this object path already has a value, only overwite
+        // it if the previous one was an NaN or a smaller value.
+        auto existing = sensorData.find(sensorPath);
+        if (existing != sensorData.end())
+        {
+            if (std::isnan(existing->second) || (tempValue > existing->second))
+            {
+                existing->second = tempValue;
+            }
+        }
+        else
+        {
+            sensorData[sensorPath] = tempValue;
+        }
+    }
 
-        dbus::OccDBusSensors::getOccDBus().setOperationalStatus(sensorPath,
-                                                                true);
+    // Now publish the values on D-Bus.
+    for (const auto& [objectPath, value] : sensorData)
+    {
+        dbus::OccDBusSensors::getOccDBus().setValue(objectPath,
+                                                    value * std::pow(10, -3));
 
-        // At this point, the sensor will be created for sure.
-        if (existingSensors.find(sensorPath) == existingSensors.end())
+        dbus::OccDBusSensors::getOccDBus().setOperationalStatus(
+            objectPath, !std::isnan(value));
+
+        if (existingSensors.find(objectPath) == existingSensors.end())
         {
             dbus::OccDBusSensors::getOccDBus().setChassisAssociation(
-                sensorPath);
+                objectPath);
         }
 
-        existingSensors[sensorPath] = id;
+        existingSensors[objectPath] = id;
     }
-    return;
 }
 
 std::optional<std::string>