control: Associate identifier with target holds

Create a zone method that associates a unique identifier to a target
hold so that if more than one hold exists, the highest target is always
used between all actions that could have set a target hold on the zone.

Change-Id: I7699769a2e271c8a63a0a0a05aef6b0888ce180a
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/json/actions/count_state_target.cpp b/control/json/actions/count_state_target.cpp
index 9f2ba5e..1017ae6 100644
--- a/control/json/actions/count_state_target.cpp
+++ b/control/json/actions/count_state_target.cpp
@@ -64,7 +64,6 @@
             }
             if (numAtState >= _count)
             {
-                zone.setTarget(_target);
                 break;
             }
         }
@@ -74,9 +73,9 @@
         }
     }
 
-    // Update zone's active fan control allowed based on action results
-    zone.setActiveAllow(ActionBase::getName() + std::to_string(_id),
-                        !(numAtState >= _count));
+    // Update zone's target hold based on action results
+    zone.setTargetHold(ActionBase::getName() + std::to_string(_id), _target,
+                       (numAtState >= _count));
 }
 
 void CountStateTarget::setCount(const json& jsonObj)
diff --git a/control/json/actions/missing_owner_target.cpp b/control/json/actions/missing_owner_target.cpp
index 68f6614..8d8a605 100644
--- a/control/json/actions/missing_owner_target.cpp
+++ b/control/json/actions/missing_owner_target.cpp
@@ -49,12 +49,8 @@
                         [&intf = group.getInterface()](const auto& member) {
                             return !Manager::hasOwner(member, intf);
                         });
-        if (isMissingOwner)
-        {
-            zone.setTarget(_target);
-        }
-        // Update group's fan control active allowed based on action results
-        zone.setActiveAllow(group.getName(), !isMissingOwner);
+        // Update zone's target hold based on action results
+        zone.setTargetHold(group.getName(), _target, isMissingOwner);
     }
 }
 
diff --git a/control/json/zone.cpp b/control/json/zone.cpp
index 6d2744f..d59fe26 100644
--- a/control/json/zone.cpp
+++ b/control/json/zone.cpp
@@ -143,6 +143,36 @@
     }
 }
 
+void Zone::setTargetHold(const std::string& ident, uint64_t target, bool hold)
+{
+    if (!hold)
+    {
+        _holds.erase(ident);
+    }
+    else
+    {
+        _holds[ident] = target;
+        _isActive = false;
+    }
+
+    auto itHoldMax = std::max_element(_holds.begin(), _holds.end(),
+                                      [](const auto& aHold, const auto& bHold) {
+                                          return aHold.second < bHold.second;
+                                      });
+    if (itHoldMax == _holds.end())
+    {
+        _isActive = true;
+    }
+    else
+    {
+        _target = itHoldMax->second;
+        for (auto& fan : _fans)
+        {
+            fan->setTarget(_target);
+        }
+    }
+}
+
 void Zone::setActiveAllow(const std::string& ident, bool isActiveAllow)
 {
     _active[ident] = isActiveAllow;
diff --git a/control/json/zone.hpp b/control/json/zone.hpp
index b47e051..d6ee3e2 100644
--- a/control/json/zone.hpp
+++ b/control/json/zone.hpp
@@ -216,6 +216,18 @@
     void setTarget(uint64_t target);
 
     /**
+     * Sets and holds all fans in the zone to the target given or releases a
+     * target hold resulting in the fans being held at the highest remaining
+     * hold target if other hold targets had been requested. When no hold
+     * targets exist, the zone returns to being active.
+     *
+     * @param[in] ident - Unique identifier for a target hold
+     * @param[in] target - Target to hold fans at
+     * @param[in] hold - Whether to hold(true) or release(false) a target hold
+     */
+    void setTargetHold(const std::string& ident, uint64_t target, bool hold);
+
+    /**
      * @brief Sets the automatic fan control allowed active state
      *
      * @param[in] ident - An identifier that affects the active state
@@ -434,6 +446,9 @@
     /* Map of active fan control allowed by a string identifier */
     std::map<std::string, bool> _active;
 
+    /* Map of target holds by a string identifier */
+    std::unordered_map<std::string, uint64_t> _holds;
+
     /* Interface to property mapping of their associated set property handler
      * function */
     static const std::map<