control: Mapped floor action can use parameters

Previously, the mapped floor action could only set floor values based on
group property values.  This commit adds support to be able to use a
manager parameter instead of a floor value.

For example:

    "floors": [
    {
        "parameter": "pcie_floor_index",
        "floors": [
        {
            "value": 1,
            "floor": 2000
        }
        ...
    }
    ...

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I874fa0f0c6b9befd74a095c31b893c5435b08abe
diff --git a/control/json/actions/mapped_floor.cpp b/control/json/actions/mapped_floor.cpp
index bca3bf5..2f077f1 100644
--- a/control/json/actions/mapped_floor.cpp
+++ b/control/json/actions/mapped_floor.cpp
@@ -89,15 +89,27 @@
 
         for (const auto& groupEntry : floors["floors"])
         {
-            if (!groupEntry.contains("group") || !groupEntry.contains("floors"))
+            if ((!groupEntry.contains("group") &&
+                 !groupEntry.contains("parameter")) ||
+                !groupEntry.contains("floors"))
             {
-                throw ActionParseError{ActionBase::getName(),
-                                       "Missing group or floors entries in "
-                                       "actions/fan_floors/floors JSON"};
+                throw ActionParseError{
+                    ActionBase::getName(),
+                    "Missing group, parameter, or floors entries in "
+                    "actions/fan_floors/floors JSON"};
             }
 
             FloorGroup fg;
-            fg.group = getGroup(groupEntry["group"].get<std::string>());
+            if (groupEntry.contains("group"))
+            {
+                fg.groupOrParameter =
+                    getGroup(groupEntry["group"].get<std::string>());
+            }
+            else
+            {
+                fg.groupOrParameter =
+                    groupEntry["parameter"].get<std::string>();
+            }
 
             for (const auto& floorEntry : groupEntry["floors"])
             {
@@ -230,9 +242,35 @@
         }
 
         // Now check each group in the tables
-        for (const auto& [group, floorGroups] : floorTable.floorGroups)
+        for (const auto& [groupOrParameter, floorGroups] :
+             floorTable.floorGroups)
         {
-            auto propertyValue = getMaxGroupValue(*group, manager);
+            std::optional<PropertyVariantType> propertyValue;
+
+            if (std::holds_alternative<std::string>(groupOrParameter))
+            {
+                propertyValue = Manager::getParameter(
+                    std::get<std::string>(groupOrParameter));
+                if (propertyValue)
+                {
+                    tryConvertToDouble(*propertyValue);
+                }
+                else
+                {
+                    log<level::ERR>(
+                        fmt::format("{}: Parameter {} specified in the JSON "
+                                    "could not be found",
+                                    ActionBase::getName(),
+                                    std::get<std::string>(groupOrParameter))
+                            .c_str());
+                }
+            }
+            else
+            {
+                propertyValue = getMaxGroupValue(
+                    *std::get<const Group*>(groupOrParameter), manager);
+            }
+
             if (!propertyValue)
             {
                 // Couldn't successfully get a value.  Results in default floor.
diff --git a/control/json/actions/mapped_floor.hpp b/control/json/actions/mapped_floor.hpp
index baf2701..3edbf3d 100644
--- a/control/json/actions/mapped_floor.hpp
+++ b/control/json/actions/mapped_floor.hpp
@@ -96,6 +96,14 @@
  * Other notes:
  *  - If a group has multiple members, they must be numeric or else
  *    the code will throw an exception.
+ *
+ *  - The group inside the floors array can also be a Manager parameter, so that
+ *    this action can operate on a parameter value set by another action.
+ *
+ *    So instead of
+ *            "group": "altitude",
+ *    it can be:
+ *            "parameter": "some_parameter"
  */
 
 class MappedFloor : public ActionBase, public ActionRegister<MappedFloor>
@@ -177,7 +185,7 @@
 
     struct FloorGroup
     {
-        const Group* group;
+        std::variant<const Group*, std::string> groupOrParameter;
         std::vector<FloorEntry> floorEntries;
     };