Create empty action groups

This introduces the framework for set speed events to have the groups
within an event assign directly on the event action. When a group is
defined directly to an action, it will be used in place of the group(s)
on the event. This allows a single event, driven by trigger(s), to call
multiple actions on different groups with differing parameters to those
actions.

Change-Id: I5c648d912641b3006a77eab27457e3ddb220c2ad
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/gen-fan-zone-defs.py b/control/gen-fan-zone-defs.py
index a6c34b6..f422522 100755
--- a/control/gen-fan-zone-defs.py
+++ b/control/gen-fan-zone-defs.py
@@ -148,6 +148,7 @@
             e += "},\n"
     e += "},\n"
 
+    e += "{Group{},\n"
     e += "std::vector<Action>{\n"
     for a in event['action']:
         if len(a['parameters']) != 0:
@@ -160,7 +161,7 @@
             else:
                 e += p + ")\n"
         e += "),\n"
-    e += "},\n"
+    e += "}},\n"
 
     e += "std::vector<Trigger>{\n"
     if ('timer' in event['triggers']) and \
diff --git a/control/templates/defs.mako b/control/templates/defs.mako
index 5356677..3a68e99 100644
--- a/control/templates/defs.mako
+++ b/control/templates/defs.mako
@@ -60,6 +60,7 @@
 %endfor
 %endfor
 },
+{Group{},
 std::vector<Action>{
 %for a in event['action']:
 %if len(a['parameters']) != 0:
@@ -76,7 +77,7 @@
 %endfor
 ),
 %endfor
-},
+}},
 std::vector<Trigger>{
     %if ('timer' in event['triggers']) and \
         (event['triggers']['timer'] is not None):
diff --git a/control/templates/fan_zone_defs.mako.cpp b/control/templates/fan_zone_defs.mako.cpp
index eaac9e3..59b302d 100644
--- a/control/templates/fan_zone_defs.mako.cpp
+++ b/control/templates/fan_zone_defs.mako.cpp
@@ -111,6 +111,7 @@
                         %endfor
                         %endfor
                         },
+                        {Group{},
                         std::vector<Action>{
                         %for i, a in enumerate(event['pc']['pcact']):
                         %if len(a['params']) != 0:
@@ -158,7 +159,7 @@
                         %else:
                         ),
                         %endif
-                        },
+                        }},
                         std::vector<Trigger>{
                             %if ('timer' in event['pc']['triggers']) and \
                                 (event['pc']['triggers']['timer'] is not None):
diff --git a/control/types.hpp b/control/types.hpp
index 4c3bec2..000eec0 100644
--- a/control/types.hpp
+++ b/control/types.hpp
@@ -59,6 +59,10 @@
                                    const Group&,
                                    const std::vector<Action>&)>;
 
+constexpr auto adGroupPos = 0;
+constexpr auto adActionsPos = 1;
+using ActionData = std::vector<std::tuple<Group, std::vector<Action>>>;
+
 constexpr auto pcPathPos = 0;
 constexpr auto pcIntfPos = 1;
 constexpr auto pcPropPos = 2;
@@ -82,13 +86,15 @@
 using TimerConf = std::tuple<std::chrono::microseconds,
                              TimerType>;
 
+// TODO Remove event group (groupPos)
+// Bind groups directly to list of actions after optimized code generated
 constexpr auto sseNamePos = 0;
 constexpr auto groupPos = 1;
 constexpr auto actionsPos = 2;
 constexpr auto triggerPos = 3;
 using SetSpeedEvent = std::tuple<std::string,
                                  Group,
-                                 std::vector<Action>,
+                                 ActionData,
                                  std::vector<Trigger>>;
 
 constexpr auto eventGroupPos = 0;
diff --git a/control/zone.cpp b/control/zone.cpp
index 32a297b..7c09727 100644
--- a/control/zone.cpp
+++ b/control/zone.cpp
@@ -325,10 +325,38 @@
         std::get<triggerPos>(event).end(),
         [this, &event](auto const& trigger)
         {
-            trigger(*this,
-                    std::get<sseNamePos>(event),
-                    std::get<groupPos>(event),
-                    std::get<actionsPos>(event));
+            if (!std::get<actionsPos>(event).empty())
+            {
+                std::for_each(
+                    std::get<actionsPos>(event).begin(),
+                    std::get<actionsPos>(event).end(),
+                    [this, &trigger, &event](auto const& action)
+                    {
+                        // Default to use group defined with action if exists
+                        if (!std::get<adGroupPos>(action).empty())
+                        {
+                            trigger(*this,
+                                    std::get<sseNamePos>(event),
+                                    std::get<adGroupPos>(action),
+                                    std::get<adActionsPos>(action));
+                        }
+                        else
+                        {
+                            trigger(*this,
+                                    std::get<sseNamePos>(event),
+                                    std::get<groupPos>(event),
+                                    std::get<adActionsPos>(action));
+                        }
+                    }
+                );
+            }
+            else
+            {
+                trigger(*this,
+                        std::get<sseNamePos>(event),
+                        std::get<groupPos>(event),
+                        {});
+            }
         }
     );
 }