Modified CcnxConsumer: retransmission after NACK or default timeout
diff --git a/apps/ccnx-consumer.cc b/apps/ccnx-consumer.cc
index dc54f92..27891b1 100644
--- a/apps/ccnx-consumer.cc
+++ b/apps/ccnx-consumer.cc
@@ -34,6 +34,11 @@
 #include "ns3/ccnx-content-object-header.h"
 
 #include <boost/ref.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/lambda/lambda.hpp>
+#include <boost/lambda/bind.hpp>
+
+namespace ll = boost::lambda;
 
 NS_LOG_COMPONENT_DEFINE ("CcnxConsumer");
 
@@ -48,6 +53,11 @@
   static TypeId tid = TypeId ("ns3::CcnxConsumer")
     .SetParent<Application> ()
     .AddConstructor<CcnxConsumer> ()
+    .AddAttribute ("StartSeq", "Initial sequence number",
+                   IntegerValue(0),
+                   MakeIntegerAccessor(&CcnxConsumer::m_seq),
+                   MakeIntegerChecker<int32_t>())
+    
     .AddAttribute ("OffTime", "Time interval between packets",
                    StringValue ("100ms"),
                    MakeTimeAccessor (&CcnxConsumer::m_offTime),
@@ -76,13 +86,23 @@
                    CcnxNameComponentsValue (),
                    MakeCcnxNameComponentsAccessor (&CcnxConsumer::m_exclude),
                    MakeCcnxNameComponentsChecker ())
-    // .AddAttribute ("Initial Nonce", "If 0 then nonce is not used",
-    //                UintegerValue(1),
-    //                MakeUintegerAccessor(&CcnxConsumer::m_initialNonce),
-    //                MakeUintegerChecker<uint32_t>())
+
+    .AddAttribute ("RTO",
+                   "Initial retransmission timeout",
+                   StringValue ("1s"),
+                   MakeTimeAccessor (&CcnxConsumer::m_rto),
+                   MakeTimeChecker ())
+    .AddAttribute ("RetxTimer",
+                   "Timeout defining how frequent retransmission timeouts should be checked",
+                   StringValue ("1s"),
+                   MakeTimeAccessor (&CcnxConsumer::GetRetxTimer, &CcnxConsumer::SetRetxTimer),
+                   MakeTimeChecker ())
+
     // .AddTraceSource ("InterestTrace", "Interests that were sent",
     //                  MakeTraceSourceAccessor (&CcnxConsumer::m_interestsTrace))
-    // .AddTraceSource ("ContentObjectTrace", "ContentObjects that were received",
+    // .AddTraceSource ("NackTrace", "NACKs received",
+    //                  MakeTraceSourceAccessor (&CcnxConsumer::m_nackTrace))
+    // .AddTraceSource ("ContentObjectTrace", "ContentObjects received",
     //                  MakeTraceSourceAccessor (&CcnxConsumer::m_contentObjectsTrace))
     ;
 
@@ -95,7 +115,48 @@
 {
   NS_LOG_FUNCTION_NOARGS ();
 }
-    
+
+void
+CcnxConsumer::SetRetxTimer (Time retxTimer)
+{
+  m_retxTimer = retxTimer;
+  if (m_retxEvent.IsRunning ())
+    m_retxEvent.Cancel (); // cancel any scheduled cleanup events
+
+  // schedule even with new timeout
+  m_retxEvent = Simulator::Schedule (m_retxTimer,
+                                     &CcnxConsumer::CheckRetxTimeout, this); 
+}
+
+Time
+CcnxConsumer::GetRetxTimer () const
+{
+  return m_retxTimer;
+}
+
+void
+CcnxConsumer::CheckRetxTimeout ()
+{
+  Time now = Simulator::Now ();
+
+  while (!m_seqTimeouts.empty ())
+    {
+      SeqTimeoutsContainer::index<i_timestamp>::type::iterator entry =
+        m_seqTimeouts.get<i_timestamp> ().begin ();
+      if (entry->time + m_rto <= now) // timeout expired?
+        {
+          m_retxSeqs.insert (entry->seq);
+          m_seqTimeouts.get<i_timestamp> ().modify (entry,
+                                                    ll::bind(&SeqTimeout::time, ll::_1) = now);
+        }
+      else
+        break; // nothing else to do. All later packets need not be retransmitted
+    }
+  
+  m_retxEvent = Simulator::Schedule (m_retxTimer,
+                                     &CcnxConsumer::CheckRetxTimeout, this); 
+}
+
 // Application Methods
 void 
 CcnxConsumer::StartApplication () // Called at time specified by Start
@@ -125,10 +186,20 @@
 CcnxConsumer::SendPacket ()
 {
   NS_LOG_FUNCTION_NOARGS ();
-    
+
+  uint32_t seq;
+  
+  if (m_retxSeqs.size () != 0)
+    {
+      seq = *m_retxSeqs.begin ();
+      m_retxSeqs.erase (m_retxSeqs.begin ());
+    }
+  else
+    seq = m_seq++;
+  
   //
   Ptr<CcnxNameComponents> nameWithSequence = Create<CcnxNameComponents> (m_interestName);
-  (*nameWithSequence) (m_seq++);
+  (*nameWithSequence) (seq);
   //
 
   CcnxInterestHeader interestHeader;
@@ -143,14 +214,23 @@
   interestHeader.SetMaxSuffixComponents (m_maxSuffixComponents);
   interestHeader.SetMinSuffixComponents (m_minSuffixComponents);
         
-  NS_LOG_INFO ("Requesting Interest: \n" << interestHeader);
+  // NS_LOG_INFO ("Requesting Interest: \n" << interestHeader);
+  NS_LOG_INFO ("> Interest for " << seq);
 
   Ptr<Packet> packet = Create<Packet> ();
   packet->AddHeader (interestHeader);
 
   m_protocolHandler (packet);
-    
+
+  std::pair<SeqTimeoutsContainer::iterator, bool>
+    res = m_seqTimeouts.insert (SeqTimeout (seq, Simulator::Now ()));
+  if (!res.second)
+    m_seqTimeouts.modify (res.first,
+                          ll::bind(&SeqTimeout::time, ll::_1) = Simulator::Now ());
+  
   m_sendEvent = Simulator::Schedule (m_offTime, &CcnxConsumer::SendPacket, this);
+
+  // \todo Trace
 }
 
 void
@@ -159,7 +239,35 @@
 {
   NS_LOG_FUNCTION (this << contentObject << payload);
 
-  NS_LOG_INFO ("Received content object: " << boost::cref(*contentObject));
+  // NS_LOG_INFO ("Received content object: " << boost::cref(*contentObject));
+  
+  uint32_t seq = boost::lexical_cast<uint32_t> (contentObject->GetName ().GetComponents ().back ());
+  NS_LOG_INFO ("< DATA for " << seq);
+
+  SeqTimeoutsContainer::iterator entry = m_seqTimeouts.find (seq);
+
+  NS_ASSERT_MSG (entry != m_seqTimeouts.end (),
+                 "Comment out this assert, if it causes problems");
+
+  if (entry != m_seqTimeouts.end ())
+    m_seqTimeouts.erase (entry);
+
+  // \todo Trace
+}
+
+void
+CcnxConsumer::OnNack (const Ptr<const CcnxInterestHeader> &interest)
+{
+  NS_LOG_FUNCTION (this << interest);
+
+  // NS_LOG_INFO ("Received NACK: " << boost::cref(*interest));
+  uint32_t seq = boost::lexical_cast<uint32_t> (interest->GetName ().GetComponents ().back ());
+  NS_LOG_INFO ("< NACK for " << seq);
+
+  // put in the queue of interests to be retransmitted
+  m_retxSeqs.insert (seq);
+
+  // \todo Trace?
 }
 
 } // namespace ns3
diff --git a/apps/ccnx-consumer.h b/apps/ccnx-consumer.h
index 792c829..ce0a20c 100644
--- a/apps/ccnx-consumer.h
+++ b/apps/ccnx-consumer.h
@@ -24,6 +24,14 @@
 #include "ccnx-app.h"
 #include "ns3/random-variable.h"
 #include "ns3/ccnx-name-components.h"
+#include "ns3/nstime.h"
+
+#include <set>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/tag.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/member.hpp>
 
 namespace ns3 
 {
@@ -35,8 +43,16 @@
         
   CcnxConsumer ();
 
-  void OnContentObject (const Ptr<const CcnxContentObjectHeader> &contentObject,
-                        const Ptr<const Packet> &payload);
+  // From CcnxApp
+  // virtual void
+  // OnInterest (const Ptr<const CcnxInterestHeader> &interest);
+
+  virtual void
+  OnNack (const Ptr<const CcnxInterestHeader> &interest);
+
+  virtual void
+  OnContentObject (const Ptr<const CcnxContentObjectHeader> &contentObject,
+                   const Ptr<const Packet> &payload);
 
 protected:
   // from CcnxApp
@@ -48,13 +64,29 @@
   
 private:
   //helpers
-  void SendPacket ();
-     
-private:
+  void
+  SendPacket ();
+
+  void
+  CheckRetxTimeout ();
+  
+  void
+  SetRetxTimer (Time retxTimer);
+
+  Time
+  GetRetxTimer () const;
+  
+protected:
   UniformVariable m_rand;
   uint32_t        m_seq;
   EventId         m_sendEvent; // Eventid of pending "send packet" event
+  Time            m_retxTimer;
+  EventId         m_retxEvent; // Event to check whether or not retransmission should be performed
 
+  Time            m_rto; // Retransmission timeout
+  Time            m_rttVar; // RTT variance
+  Time            m_sRtt; // smoothed RTT
+  
   Time               m_offTime;
   CcnxNameComponents m_interestName;
   Time               m_interestLifeTime;
@@ -62,6 +94,41 @@
   int32_t            m_maxSuffixComponents;
   bool               m_childSelector;
   CcnxNameComponents m_exclude;
+
+  struct RetxSeqsContainer :
+    public std::set<uint32_t> { };
+  
+  RetxSeqsContainer m_retxSeqs; // ordered set of sequence numbers to be retransmitted
+
+  struct SeqTimeout
+  {
+    SeqTimeout (uint32_t _seq, Time _time) : seq (_seq), time (_time) { }
+    
+    uint32_t seq;
+    Time time;
+
+    bool operator < (const SeqTimeout &st) { return time < st.time || (time == st.time && seq < st.seq); }
+  };
+
+  class i_seq { };
+  class i_timestamp { }; 
+  
+  struct SeqTimeoutsContainer :
+    public boost::multi_index::multi_index_container<
+    SeqTimeout,
+    boost::multi_index::indexed_by<
+      boost::multi_index::ordered_unique<
+        boost::multi_index::tag<i_seq>,
+        boost::multi_index::member<SeqTimeout, uint32_t, &SeqTimeout::seq>
+        >,
+      boost::multi_index::ordered_non_unique<
+        boost::multi_index::tag<i_timestamp>,
+        boost::multi_index::member<SeqTimeout, Time, &SeqTimeout::time>
+        >
+      >
+    > { } ;
+
+  SeqTimeoutsContainer m_seqTimeouts;
 };
 
 } // namespace ns3