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