helper: Experimental extension of ndn::StackHelper to enable customization of NetDeviceFace (e.g., creating of custom faces based on different NetDevice's)
diff --git a/helper/ndn-stack-helper.cc b/helper/ndn-stack-helper.cc
index 5f7f7a6..04ceb46 100644
--- a/helper/ndn-stack-helper.cc
+++ b/helper/ndn-stack-helper.cc
@@ -16,7 +16,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Author:  Alexander Afanasyev <alexander.afanasyev@ucla.edu>
- *          Ilya Moiseenko <iliamo@cs.ucla.edu> 
+ *          Ilya Moiseenko <iliamo@cs.ucla.edu>
  */
 
 #include "ns3/assert.h"
@@ -34,6 +34,7 @@
 #include "ns3/core-config.h"
 #include "ns3/point-to-point-net-device.h"
 #include "ns3/point-to-point-helper.h"
+#include "ns3/callback.h"
 
 #include "../model/ndn-net-device-face.h"
 #include "../model/ndn-l3-protocol.h"
@@ -61,7 +62,7 @@
 
 namespace ns3 {
 namespace ndn {
-    
+
 StackHelper::StackHelper ()
   : m_limitsEnabled (false)
   , m_needSetDefaultRoutes (false)
@@ -71,8 +72,11 @@
   m_contentStoreFactory.SetTypeId ("ns3::ndn::cs::Lru");
   m_fibFactory.         SetTypeId ("ns3::ndn::fib::Default");
   m_pitFactory.         SetTypeId ("ns3::ndn::pit::Persistent");
+
+  m_netDeviceCallbacks.push_back (std::make_pair (PointToPointNetDevice::GetTypeId (), MakeCallback (&StackHelper::PointToPointNetDeviceCallback, this)));
+  // default callback will be fired if non of others callbacks fit or did the job
 }
-    
+
 StackHelper::~StackHelper ()
 {
 }
@@ -93,7 +97,7 @@
       m_ndnFactory.Set (attr4, StringValue (value4));
 }
 
-void 
+void
 StackHelper::SetForwardingStrategy (const std::string &strategy,
                                     const std::string &attr1, const std::string &value1,
                                     const std::string &attr2, const std::string &value2,
@@ -207,10 +211,10 @@
 {
   // NS_ASSERT_MSG (m_forwarding, "SetForwardingHelper() should be set prior calling Install() method");
   Ptr<FaceContainer> faces = Create<FaceContainer> ();
-  
+
   if (node->GetObject<L3Protocol> () != 0)
     {
-      NS_FATAL_ERROR ("StackHelper::Install (): Installing " 
+      NS_FATAL_ERROR ("StackHelper::Install (): Installing "
                       "a NdnStack to a node with an existing Ndn object");
       return 0;
     }
@@ -224,7 +228,7 @@
 
   // Create and aggregate PIT
   ndn->AggregateObject (m_pitFactory.Create<Pit> ());
-  
+
   // Create and aggregate forwarding strategy
   ndn->AggregateObject (m_strategyFactory.Create<ForwardingStrategy> ());
 
@@ -233,7 +237,7 @@
 
   // Aggregate L3Protocol on node
   node->AggregateObject (ndn);
-  
+
   for (uint32_t index=0; index < node->GetNDevices (); index++)
     {
       Ptr<NetDevice> device = node->GetDevice (index);
@@ -242,7 +246,24 @@
       // if (DynamicCast<LoopbackNetDevice> (device) != 0)
       //   continue; // don't create face for a LoopbackNetDevice
 
-      Ptr<NetDeviceFace> face = CreateObject<NetDeviceFace> (node, device);
+      Ptr<NetDeviceFace> face;
+
+      for (std::list< std::pair<TypeId, NetDeviceFaceCreateCallback> >::const_iterator item = m_netDeviceCallbacks.begin ();
+           item != m_netDeviceCallbacks.end ();
+           item++)
+        {
+          if (device->GetInstanceTypeId () == item->first ||
+              device->GetInstanceTypeId ().IsChildOf (item->first))
+            {
+              face = item->second (node, device);
+              if (face != 0)
+                break;
+            }
+        }
+      if (face == 0)
+        {
+          face = DefaultNetDeviceCallback (node, device);
+        }
 
       ndn->AddFace (face);
       NS_LOG_LOGIC ("Node " << node->GetId () << ": added NetDeviceFace as face #" << *face);
@@ -250,49 +271,75 @@
       if (m_needSetDefaultRoutes)
         {
           // default route with lowest priority possible
-          AddRoute (node, "/", StaticCast<Face> (face), std::numeric_limits<int32_t>::max ()); 
+          AddRoute (node, "/", StaticCast<Face> (face), std::numeric_limits<int32_t>::max ());
         }
-      
-      if (m_limitsEnabled)
-        {
-          Ptr<Limits> limits = face->GetObject<Limits> ();
-          if (limits == 0)
-            {
-              NS_FATAL_ERROR ("Limits are enabled, but the selected forwarding strategy does not support limits. Please revise your scenario");
-              exit (1);
-            }
-          
-          NS_LOG_INFO ("Limits are enabled");
-          Ptr<PointToPointNetDevice> p2p = DynamicCast<PointToPointNetDevice> (device);
-          if (p2p != 0)
-            {
-              // Setup bucket filtering
-              // Assume that we know average data packet size, and this size is equal default size
-              // Set maximum buckets (averaging over 1 second)
-      
-              DataRateValue dataRate; device->GetAttribute ("DataRate", dataRate);
-              TimeValue linkDelay;   device->GetChannel ()->GetAttribute ("Delay", linkDelay);
-          
-              NS_LOG_INFO("DataRate for this link is " << dataRate.Get());
 
-              double maxInterestPackets = 1.0  * dataRate.Get ().GetBitRate () / 8.0 / (m_avgContentObjectSize + m_avgInterestSize);
-              // NS_LOG_INFO ("Max packets per second: " << maxInterestPackets);
-              // NS_LOG_INFO ("Max burst: " << m_avgRtt.ToDouble (Time::S) * maxInterestPackets);
-              NS_LOG_INFO ("MaxLimit: " << (int)(m_avgRtt.ToDouble (Time::S) * maxInterestPackets));
-
-              // Set max to BDP
-              limits->SetLimits (maxInterestPackets, m_avgRtt.ToDouble (Time::S));
-              limits->SetLinkDelay (linkDelay.Get ().ToDouble (Time::S));
-            }
-        }
-        
       face->SetUp ();
       faces->Add (face);
     }
-    
+
   return faces;
 }
 
+void
+StackHelper::AddNetDeviceFaceCreateCallback (TypeId netDeviceType, StackHelper::NetDeviceFaceCreateCallback callback)
+{
+  m_netDeviceCallbacks.push_back (std::make_pair (netDeviceType, callback));
+}
+
+
+Ptr<NetDeviceFace>
+StackHelper::DefaultNetDeviceCallback (Ptr<Node> node, Ptr<NetDevice> netDevice) const
+{
+  NS_LOG_DEBUG ("Creating default NetDeviceFace on node " << node->GetId ());
+
+  return CreateObject<NetDeviceFace> (node, netDevice);
+}
+
+Ptr<NetDeviceFace>
+StackHelper::PointToPointNetDeviceCallback (Ptr<Node> node, Ptr<NetDevice> device) const
+{
+  NS_LOG_DEBUG ("Creating point-to-point NetDeviceFace on node " << node->GetId ());
+
+  Ptr<NetDeviceFace> face = CreateObject<NetDeviceFace> (node, device);
+
+  if (m_limitsEnabled)
+    {
+      Ptr<Limits> limits = face->GetObject<Limits> ();
+      if (limits == 0)
+        {
+          NS_FATAL_ERROR ("Limits are enabled, but the selected forwarding strategy does not support limits. Please revise your scenario");
+          exit (1);
+        }
+
+      NS_LOG_INFO ("Limits are enabled");
+      Ptr<PointToPointNetDevice> p2p = DynamicCast<PointToPointNetDevice> (device);
+      if (p2p != 0)
+        {
+          // Setup bucket filtering
+          // Assume that we know average data packet size, and this size is equal default size
+          // Set maximum buckets (averaging over 1 second)
+
+          DataRateValue dataRate; device->GetAttribute ("DataRate", dataRate);
+          TimeValue linkDelay;   device->GetChannel ()->GetAttribute ("Delay", linkDelay);
+
+          NS_LOG_INFO("DataRate for this link is " << dataRate.Get());
+
+          double maxInterestPackets = 1.0  * dataRate.Get ().GetBitRate () / 8.0 / (m_avgContentObjectSize + m_avgInterestSize);
+          // NS_LOG_INFO ("Max packets per second: " << maxInterestPackets);
+          // NS_LOG_INFO ("Max burst: " << m_avgRtt.ToDouble (Time::S) * maxInterestPackets);
+          NS_LOG_INFO ("MaxLimit: " << (int)(m_avgRtt.ToDouble (Time::S) * maxInterestPackets));
+
+          // Set max to BDP
+          limits->SetLimits (maxInterestPackets, m_avgRtt.ToDouble (Time::S));
+          limits->SetLinkDelay (linkDelay.Get ().ToDouble (Time::S));
+        }
+    }
+
+  return face;
+}
+
+
 Ptr<FaceContainer>
 StackHelper::Install (const std::string &nodeName) const
 {
@@ -330,7 +377,7 @@
 {
   Ptr<Node> node = Names::Find<Node> (nodeName);
   NS_ASSERT_MSG (node != 0, "Node [" << nodeName << "] does not exist");
-  
+
   Ptr<L3Protocol>     ndn = node->GetObject<L3Protocol> ();
   NS_ASSERT_MSG (ndn != 0, "Ndn stack should be installed on the node");
 
diff --git a/helper/ndn-stack-helper.h b/helper/ndn-stack-helper.h
index 0c036d4..25764d9 100644
--- a/helper/ndn-stack-helper.h
+++ b/helper/ndn-stack-helper.h
@@ -16,7 +16,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Author:  Alexander Afanasyev <alexander.afanasyev@ucla.edu>
- *          Ilya Moiseenko <iliamo@cs.ucla.edu> 
+ *          Ilya Moiseenko <iliamo@cs.ucla.edu>
  */
 
 #ifndef NDN_STACK_HELPER_H
@@ -57,14 +57,14 @@
  * attribute or a set of functionality that may be of interest to many other
  * classes.
  */
-class StackHelper 
+class StackHelper
 {
 public:
   /**
    * \brief Create a new NdnStackHelper with a default NDN_FLOODING forwarding stategy
    */
   StackHelper();
-  
+
   /**
    * \brief Destroy the NdnStackHelper
    */
@@ -78,8 +78,8 @@
                       const std::string &attr2 = "", const std::string &value2 = "",
                       const std::string &attr3 = "", const std::string &value3 = "",
                       const std::string &attr4 = "", const std::string &value4 = "");
-  
-  
+
+
   /**
    * @brief Set forwarding strategy class and its attributes
    * @param forwardingStrategyClass string containing name of the forwarding strategy class
@@ -116,7 +116,7 @@
           const std::string &attr2 = "", const std::string &value2 = "",
           const std::string &attr3 = "", const std::string &value3 = "",
           const std::string &attr4 = "", const std::string &value4 = "");
-  
+
   /**
    * @brief Set FIB class and its attributes
    * @param pitClass string, representing class of FIB
@@ -128,6 +128,21 @@
           const std::string &attr3 = "", const std::string &value3 = "",
           const std::string &attr4 = "", const std::string &value4 = "");
 
+  typedef Callback< Ptr<NetDeviceFace>, Ptr<Node>, Ptr<NetDevice> > NetDeviceFaceCreateCallback;
+
+  /**
+   * @brief Add callback to create and configure instance of the face, based on supplied Ptr<Node> and Ptr<NetDevice>
+   *
+   * It is possible to set up several callbacks for different NetDevice types.
+   *
+   * Currently, there is only one specialized callback for PointToPointNetDevice, which creates face and sets limits (if enabled)
+   * based on PointToPoint link parameters
+   *
+   * If none of the callbacks fit the TypeId of NetDevice, a default callback is used (DefaultNetDeviceCallback)
+   */
+  void
+  AddNetDeviceFaceCreateCallback (TypeId netDeviceType, NetDeviceFaceCreateCallback callback);
+
   /**
    * @brief Enable Interest limits (disabled by default)
    *
@@ -138,13 +153,13 @@
    */
   void
   EnableLimits (bool enable = true, Time avgRtt=Seconds(0.1), uint32_t avgContentObject=1100, uint32_t avgInterest=40);
-  
+
   /**
    * \brief Install Ndn stack on the node
    *
    * This method will assert if called on a node that already has Ndn object
    * installed on it
-   * 
+   *
    * \param nodeName The name of the node on which to install the stack.
    *
    * \returns list of installed faces in the form of a smart pointer
@@ -158,7 +173,7 @@
    *
    * This method will assert if called on a node that already has Ndn object
    * installed on it
-   * 
+   *
    * \param node The node on which to install the stack.
    *
    * \returns list of installed faces in the form of a smart pointer
@@ -172,7 +187,7 @@
    *
    * The program will assert if this method is called on a container with a node
    * that already has an ndn object aggregated to it.
-   * 
+   *
    * \param c NodeContainer that holds the set of nodes on which to install the
    * new stacks.
    *
@@ -245,7 +260,7 @@
    */
   static void
   AddRoute (const std::string &nodeName, const std::string &prefix, const std::string &otherNodeName, int32_t metric);
-  
+
   /**
    * \brief Set flag indicating necessity to install default routes in FIB
    */
@@ -253,21 +268,30 @@
   SetDefaultRoutes (bool needSet);
 
 private:
+  Ptr<NetDeviceFace>
+  DefaultNetDeviceCallback (Ptr<Node> node, Ptr<NetDevice> netDevice) const;
+
+  Ptr<NetDeviceFace>
+  PointToPointNetDeviceCallback (Ptr<Node> node, Ptr<NetDevice> netDevice) const;
+
+private:
   StackHelper (const StackHelper &);
   StackHelper &operator = (const StackHelper &o);
-  
+
 private:
   ObjectFactory m_ndnFactory;
   ObjectFactory m_strategyFactory;
   ObjectFactory m_contentStoreFactory;
   ObjectFactory m_pitFactory;
   ObjectFactory m_fibFactory;
-  
+
   bool     m_limitsEnabled;
   Time     m_avgRtt;
   uint32_t m_avgContentObjectSize;
   uint32_t m_avgInterestSize;
-  bool     m_needSetDefaultRoutes;  
+  bool     m_needSetDefaultRoutes;
+
+  std::list< std::pair<TypeId, NetDeviceFaceCreateCallback> > m_netDeviceCallbacks;
 };
 
 } // namespace ndn