control: Optimize loading/configuring event groups

The available groups configured can statically be loaded within the
event object since each event instance will configure its specific set
of groups from what's available.

Event actions use groups configured on the event or include groups
configured on the action directly with those configured on the event.
This can be done in a single function to setup the groups.

Change-Id: I87cb2949f803ca63c66658a944fe4d9eeabb7a3f
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/json/event.cpp b/control/json/event.cpp
index 4adced8..7bb26ec 100644
--- a/control/json/event.cpp
+++ b/control/json/event.cpp
@@ -38,7 +38,6 @@
 using namespace phosphor::logging;
 
 Event::Event(const json& jsonObj, Manager* mgr,
-             std::map<configKey, std::unique_ptr<Group>>& groups,
              std::map<configKey, std::unique_ptr<Zone>>& zones) :
     ConfigBase(jsonObj),
     _bus(util::SDBusPlus::getBus()), _manager(mgr), _zones(zones)
@@ -49,21 +48,27 @@
         // Event groups are optional
         if (jsonObj.contains("groups"))
         {
-            setGroups(jsonObj, groups);
+            setGroups(jsonObj, _groups);
         }
         // Event actions are optional
         if (jsonObj.contains("actions"))
         {
-            setActions(jsonObj, groups);
+            setActions(jsonObj);
         }
         setTriggers(jsonObj);
     }
     else
     {
-        setPrecond(jsonObj, groups);
+        setPrecond(jsonObj);
     }
 }
 
+auto& Event::getAvailGroups()
+{
+    static auto groups = Manager::getConfig<Group>(true);
+    return groups;
+}
+
 void Event::configGroup(Group& group, const json& jsonObj)
 {
     if (!jsonObj.contains("interface") || !jsonObj.contains("property") ||
@@ -99,8 +104,7 @@
     }
 }
 
-void Event::setPrecond(const json& jsonObj,
-                       std::map<configKey, std::unique_ptr<Group>>& groups)
+void Event::setPrecond(const json& jsonObj)
 {
     const auto& precond = jsonObj["precondition"];
     if (!precond.contains("name") || !precond.contains("groups") ||
@@ -111,39 +115,41 @@
         throw std::runtime_error(
             "Missing required event precondition attributes");
     }
-    setGroups(precond, groups);
+    setGroups(precond, _groups);
     setTriggers(precond);
 }
 
-void Event::setGroups(const json& jsonObj,
-                      std::map<configKey, std::unique_ptr<Group>>& groups)
+void Event::setGroups(const json& jsonObj, std::vector<Group>& groups)
 {
+    auto& availGroups = getAvailGroups();
     for (const auto& jsonGrp : jsonObj["groups"])
     {
         if (!jsonGrp.contains("name"))
         {
-            log<level::ERR>("Missing required group name attribute",
+            auto msg = fmt::format(
+                "Missing required group name attribute in event {}", getName());
+            log<level::ERR>(msg.c_str(),
                             entry("JSON=%s", jsonGrp.dump().c_str()));
-            throw std::runtime_error("Missing required group name attribute");
+            throw std::runtime_error(msg.c_str());
         }
 
         configKey eventProfile =
             std::make_pair(jsonGrp["name"].get<std::string>(), _profiles);
-        auto grpEntry = std::find_if(
-            groups.begin(), groups.end(), [&eventProfile](const auto& grp) {
-                return Manager::inConfig(grp.first, eventProfile);
-            });
-        if (grpEntry != groups.end())
+        auto grpEntry =
+            std::find_if(availGroups.begin(), availGroups.end(),
+                         [&eventProfile](const auto& grp) {
+                             return Manager::inConfig(grp.first, eventProfile);
+                         });
+        if (grpEntry != availGroups.end())
         {
             auto group = Group(*grpEntry->second);
             configGroup(group, jsonGrp);
-            _groups.emplace_back(group);
+            groups.emplace_back(group);
         }
     }
 }
 
-void Event::setActions(const json& jsonObj,
-                       std::map<configKey, std::unique_ptr<Group>>& groups)
+void Event::setActions(const json& jsonObj)
 {
     for (const auto& jsonAct : jsonObj["actions"])
     {
@@ -159,30 +165,7 @@
         auto actionGroups = _groups;
         if (jsonAct.contains("groups"))
         {
-            for (const auto& jsonGrp : jsonAct["groups"])
-            {
-                if (!jsonGrp.contains("name"))
-                {
-                    log<level::ERR>("Missing required group name attribute",
-                                    entry("JSON=%s", jsonGrp.dump().c_str()));
-                    throw std::runtime_error(
-                        "Missing required group name attribute");
-                }
-
-                configKey eventProfile = std::make_pair(
-                    jsonGrp["name"].get<std::string>(), _profiles);
-                auto grpEntry = std::find_if(groups.begin(), groups.end(),
-                                             [&eventProfile](const auto& grp) {
-                                                 return Manager::inConfig(
-                                                     grp.first, eventProfile);
-                                             });
-                if (grpEntry != groups.end())
-                {
-                    auto group = Group(*grpEntry->second);
-                    configGroup(group, jsonGrp);
-                    actionGroups.emplace_back(group);
-                }
-            }
+            setGroups(jsonAct, actionGroups);
         }
         if (actionGroups.empty())
         {
diff --git a/control/json/event.hpp b/control/json/event.hpp
index 2608014..a37d9be 100644
--- a/control/json/event.hpp
+++ b/control/json/event.hpp
@@ -75,11 +75,9 @@
      *
      * @param[in] jsonObj - JSON object
      * @param[in] mgr - Manager of this event
-     * @param[in] groups - Available groups that can be used
      * @param[in] zones - Reference to the configured zones
      */
     Event(const json& jsonObj, Manager* mgr,
-          std::map<configKey, std::unique_ptr<Group>>& groups,
           std::map<configKey, std::unique_ptr<Zone>>& zones);
 
     /**
@@ -93,16 +91,6 @@
     }
 
     /**
-     * @brief Get the groups
-     *
-     * @return List of groups associated with the event
-     */
-    inline const auto& getGroups() const
-    {
-        return _groups;
-    }
-
-    /**
      * @brief Get the actions
      *
      * @return List of actions to perform for the event
@@ -132,6 +120,13 @@
     std::vector<std::unique_ptr<ActionBase>> _actions;
 
     /**
+     * @brief Load the groups available to be configured on events
+     *
+     * @return Groups available to be configured on events from `groups.json`
+     */
+    static auto& getAvailGroups() __attribute__((pure));
+
+    /**
      * @brief Parse group parameters and configure a group object
      *
      * @param[in] group - Group object to get configured
@@ -145,34 +140,29 @@
      * @brief Parse and set the event's precondition(OPTIONAL)
      *
      * @param[in] jsonObj - JSON object for the event
-     * @param[in] groups - Available groups that can be used
      *
      * Sets the precondition of the event in order to be enabled
      */
-    void setPrecond(const json& jsonObj,
-                    std::map<configKey, std::unique_ptr<Group>>& groups);
+    void setPrecond(const json& jsonObj);
 
     /**
      * @brief Parse and set the event's groups(OPTIONAL)
      *
      * @param[in] jsonObj - JSON object for the event
-     * @param[in] groups - Available groups that can be used
+     * @param[out] groups - List of groups to be configured
      *
      * Sets the list of groups associated with the event
      */
-    void setGroups(const json& jsonObj,
-                   std::map<configKey, std::unique_ptr<Group>>& groups);
+    void setGroups(const json& jsonObj, std::vector<Group>& groups);
 
     /**
      * @brief Parse and set the event's actions(OPTIONAL)
      *
      * @param[in] jsonObj - JSON object for the event
-     * @param[in] groups - Available groups that can be used
      *
      * Sets the list of actions to perform for the event
      */
-    void setActions(const json& jsonObj,
-                    std::map<configKey, std::unique_ptr<Group>>& groups);
+    void setActions(const json& jsonObj);
 
     /**
      * @brief Parse and set the event's triggers
diff --git a/control/json/manager.cpp b/control/json/manager.cpp
index 37b8244..1a5203f 100644
--- a/control/json/manager.cpp
+++ b/control/json/manager.cpp
@@ -93,11 +93,8 @@
         }
     }
 
-    // Load the configured groups that are copied into events where they're used
-    auto groups = getConfig<Group>(true);
-
     // Load any events configured
-    _events = getConfig<Event>(true, this, groups, _zones);
+    _events = getConfig<Event>(true, this, _zones);
 
     _bus.request_name(CONTROL_BUSNAME);
 }