control: action to override a single fan in a zone - Lock fan in Zone

The override Action can only reference fans by name. Thus, Zone is
modified to apply a target lock to a specific fan given by name.
Additionally, the unlock method must pass the target value to allow
support for concurrent locks.

Signed-off-by: Mike Capps <mikepcapps@gmail.com>
Change-Id: Ic2ea81f318fb2c866198a4b24e38cc679147afc5
diff --git a/control/json/fan.hpp b/control/json/fan.hpp
index 2a10846..116e50f 100644
--- a/control/json/fan.hpp
+++ b/control/json/fan.hpp
@@ -42,6 +42,8 @@
  */
 class Fan : public ConfigBase
 {
+    friend class Zone;
+
   public:
     /* JSON file name for fans */
     static constexpr auto confFileName = "fans.json";
diff --git a/control/json/zone.cpp b/control/json/zone.cpp
index 67970a8..f1c5a44 100644
--- a/control/json/zone.cpp
+++ b/control/json/zone.cpp
@@ -1,5 +1,5 @@
 /**
- * Copyright © 2020 IBM Corporation
+ * Copyright © 2022 IBM Corporation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -144,6 +144,50 @@
     }
 }
 
+void Zone::lockFanTarget(const std::string& fname, uint64_t target)
+{
+    auto fanItr =
+        std::find_if(_fans.begin(), _fans.end(), [&fname](const auto& fan) {
+            return fan->getName() == fname;
+        });
+
+    if (_fans.end() != fanItr)
+    {
+        (*fanItr)->lockTarget(target);
+    }
+    else
+    {
+        log<level::DEBUG>(
+            fmt::format("Configured fan {} not found in zone {} to lock target",
+                        fname, getName())
+                .c_str());
+    }
+}
+
+void Zone::unlockFanTarget(const std::string& fname, uint64_t target)
+{
+    auto fanItr =
+        std::find_if(_fans.begin(), _fans.end(), [&fname](const auto& fan) {
+            return fan->getName() == fname;
+        });
+
+    if (_fans.end() != fanItr)
+    {
+        (*fanItr)->unlockTarget(target);
+
+        // attempt to resume Zone target on fan
+        (*fanItr)->setTarget(getTarget());
+    }
+    else
+    {
+        log<level::DEBUG>(
+            fmt::format(
+                "Configured fan {} not found in zone {} to unlock target",
+                fname, getName())
+                .c_str());
+    }
+}
+
 void Zone::setTargetHold(const std::string& ident, uint64_t target, bool hold)
 {
     using namespace std::string_literals;
diff --git a/control/json/zone.hpp b/control/json/zone.hpp
index 4063bd5..d94e064 100644
--- a/control/json/zone.hpp
+++ b/control/json/zone.hpp
@@ -1,5 +1,5 @@
 /**
- * Copyright © 2020 IBM Corporation
+ * Copyright © 2022 IBM Corporation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -216,6 +216,22 @@
     void setTarget(uint64_t target);
 
     /**
+     * Add a target lock for the specified fan.
+     *
+     * @param[in] fname - Fan to request/add the target lock
+     * @param[in] target - Target to register
+     */
+    void lockFanTarget(const std::string& fname, uint64_t target);
+
+    /**
+     * Remove target lock for specific fan.
+     *
+     * @param[in] fname - Fan to remove lock from
+     * @param[in] target- Target to de-register
+     */
+    void unlockFanTarget(const std::string& fname, 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