chunks: Print summary for all pipeline types

refs #4299

Change-Id: Ia8f7d2a57a030fef6e1d6b2d75c9d03af679a724
diff --git a/tools/chunks/catchunks/pipeline-interests-aimd.cpp b/tools/chunks/catchunks/pipeline-interests-aimd.cpp
index a20988b..2e3da09 100644
--- a/tools/chunks/catchunks/pipeline-interests-aimd.cpp
+++ b/tools/chunks/catchunks/pipeline-interests-aimd.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2016-2017, Regents of the University of California,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University.
@@ -22,6 +22,7 @@
  *
  * @author Shuo Yang
  * @author Weiwei Liu
+ * @author Chavoosh Ghasemi
  */
 
 #include "pipeline-interests-aimd.hpp"
@@ -40,12 +41,10 @@
   , m_scheduler(m_face.getIoService())
   , m_checkRtoEvent(m_scheduler)
   , m_nextSegmentNo(0)
-  , m_receivedSize(0)
   , m_highData(0)
   , m_highInterest(0)
   , m_recPoint(0)
   , m_nInFlight(0)
-  , m_nReceived(0)
   , m_nLossEvents(0)
   , m_nRetransmitted(0)
   , m_cwnd(m_options.initCwnd)
@@ -66,9 +65,6 @@
 void
 PipelineInterestsAimd::doRun()
 {
-  // count the excluded segment
-  m_nReceived++;
-
   // schedule the event to check retransmission timer
   m_checkRtoEvent = m_scheduler.scheduleEvent(m_options.rtoCheckInterval, [this] { checkRto(); });
 
@@ -221,7 +217,8 @@
     if (m_hasFailure && m_lastSegmentNo >= m_failedSegNo) {
       // previously failed segment is part of the content
       return onFailure(m_failureReason);
-    } else {
+    }
+    else {
       m_hasFailure = false;
     }
   }
@@ -379,7 +376,8 @@
 {
   if (m_cwnd < m_ssthresh) {
     m_cwnd += m_options.aiStep; // additive increase
-  } else {
+  }
+  else {
     m_cwnd += m_options.aiStep / std::floor(m_cwnd); // congestion avoidance
   }
 
@@ -424,41 +422,11 @@
 void
 PipelineInterestsAimd::printSummary() const
 {
-  Milliseconds timeElapsed = time::steady_clock::now() - getStartTime();
-  double throughput = (8 * m_receivedSize * 1000) / timeElapsed.count();
-
-  int pow = 0;
-  std::string throughputUnit;
-  while (throughput >= 1000.0 && pow < 4) {
-    throughput /= 1000.0;
-    pow++;
-  }
-  switch (pow) {
-    case 0:
-      throughputUnit = "bit/s";
-      break;
-    case 1:
-      throughputUnit = "kbit/s";
-      break;
-    case 2:
-      throughputUnit = "Mbit/s";
-      break;
-    case 3:
-      throughputUnit = "Gbit/s";
-      break;
-    case 4:
-      throughputUnit = "Tbit/s";
-      break;
-  }
-
-  std::cerr << "\nAll segments have been received.\n"
-            << "Time elapsed: " << timeElapsed << "\n"
-            << "Total # of segments received: " << m_nReceived << "\n"
-            << "Total # of packet loss events: " << m_nLossEvents << "\n"
+  PipelineInterests::printSummary();
+  std::cerr << "Total # of packet loss events: " << m_nLossEvents << "\n"
             << "Packet loss rate: "
             << static_cast<double>(m_nLossEvents) / static_cast<double>(m_nReceived) << "\n"
-            << "Total # of retransmitted segments: " << m_nRetransmitted << "\n"
-            << "Goodput: " << throughput << " " << throughputUnit << "\n";
+            << "Total # of retransmitted segments: " << m_nRetransmitted << "\n";
 }
 
 std::ostream&
diff --git a/tools/chunks/catchunks/pipeline-interests-aimd.hpp b/tools/chunks/catchunks/pipeline-interests-aimd.hpp
index aa19d3f..3248100 100644
--- a/tools/chunks/catchunks/pipeline-interests-aimd.hpp
+++ b/tools/chunks/catchunks/pipeline-interests-aimd.hpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2016-2017, Regents of the University of California,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University.
@@ -22,6 +22,7 @@
  *
  * @author Shuo Yang
  * @author Weiwei Liu
+ * @author Chavoosh Ghasemi
  */
 
 #ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_AIMD_HPP
@@ -190,7 +191,7 @@
   cancelInFlightSegmentsGreaterThan(uint64_t segNo);
 
   void
-  printSummary() const;
+  printSummary() const final;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   const Options m_options;
@@ -199,7 +200,6 @@
   scheduler::ScopedEventId m_checkRtoEvent;
 
   uint64_t m_nextSegmentNo;
-  size_t m_receivedSize;
 
   uint64_t m_highData; ///< the highest segment number of the Data packet the consumer has received so far
   uint64_t m_highInterest; ///< the highest segment number of the Interests the consumer has sent so far
@@ -207,7 +207,6 @@
                        ///< it remains fixed until the next packet loss event happens
 
   int64_t m_nInFlight; ///< # of segments in flight
-  int64_t m_nReceived; ///< # of segments received
   int64_t m_nLossEvents; ///< # of loss events occurred
   int64_t m_nRetransmitted; ///< # of segments retransmitted
 
diff --git a/tools/chunks/catchunks/pipeline-interests-fixed-window.cpp b/tools/chunks/catchunks/pipeline-interests-fixed-window.cpp
index 5dcb471..04cbf03 100644
--- a/tools/chunks/catchunks/pipeline-interests-fixed-window.cpp
+++ b/tools/chunks/catchunks/pipeline-interests-fixed-window.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2016-2017, Regents of the University of California,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University.
@@ -24,6 +24,7 @@
  * @author Steve DiBenedetto
  * @author Andrea Tosatto
  * @author Davide Pesavento
+ * @author Chavoosh Ghasemi
  */
 
 #include "pipeline-interests-fixed-window.hpp"
@@ -122,6 +123,9 @@
   if (m_options.isVerbose)
     std::cerr << "Received segment #" << getSegmentFromPacket(data) << std::endl;
 
+  m_nReceived++;
+  m_receivedSize += data.getContent().value_size();
+
   onData(interest, data);
 
   if (!m_hasFinalBlockId && !data.getFinalBlockId().empty()) {
@@ -143,7 +147,16 @@
     }
   }
 
-  fetchNextSegment(pipeNo);
+  BOOST_ASSERT(m_nReceived > 0);
+  if (m_hasFinalBlockId &&
+      static_cast<uint64_t>(m_nReceived - 1) >= m_lastSegmentNo) { // all segments have been received
+    if (m_options.isVerbose) {
+      printSummary();
+    }
+  }
+  else {
+    fetchNextSegment(pipeNo);
+  }
 }
 
 void PipelineInterestsFixedWindow::handleFail(const std::string& reason, std::size_t pipeNo)
diff --git a/tools/chunks/catchunks/pipeline-interests-fixed-window.hpp b/tools/chunks/catchunks/pipeline-interests-fixed-window.hpp
index 2da40e6..81b7d72 100644
--- a/tools/chunks/catchunks/pipeline-interests-fixed-window.hpp
+++ b/tools/chunks/catchunks/pipeline-interests-fixed-window.hpp
@@ -1,8 +1,8 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2016,  Regents of the University of California,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University.
+/*
+ * Copyright (c) 2016-2017,  Regents of the University of California,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -24,8 +24,12 @@
  * @author Steve DiBenedetto
  * @author Andrea Tosatto
  * @author Davide Pesavento
+ * @author Chavoosh Ghasemi
  */
 
+#ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_FIXED_WINDOW_HPP
+#define NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_FIXED_WINDOW_HPP
+
 #include "options.hpp"
 #include "pipeline-interests.hpp"
 
@@ -116,3 +120,5 @@
 
 } // namespace chunks
 } // namespace ndn
+
+#endif // NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_FIXED_WINDOW_HPP
diff --git a/tools/chunks/catchunks/pipeline-interests.cpp b/tools/chunks/catchunks/pipeline-interests.cpp
index dd6640d..965492f 100644
--- a/tools/chunks/catchunks/pipeline-interests.cpp
+++ b/tools/chunks/catchunks/pipeline-interests.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2016-2017, Regents of the University of California,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University.
@@ -25,6 +25,7 @@
  * @author Andrea Tosatto
  * @author Davide Pesavento
  * @author Weiwei Liu
+ * @author Chavoosh Ghasemi
  */
 
 #include "pipeline-interests.hpp"
@@ -36,6 +37,8 @@
   : m_face(face)
   , m_lastSegmentNo(0)
   , m_excludedSegmentNo(0)
+  , m_receivedSize(0)
+  , m_nReceived(0)
   , m_hasFinalBlockId(false)
   , m_isStopping(false)
 {
@@ -60,6 +63,8 @@
   // record the start time of the pipeline
   m_startTime = time::steady_clock::now();
 
+  m_nReceived++;
+  m_receivedSize += data.getContent().value_size();
   doRun();
 }
 
@@ -85,5 +90,42 @@
     m_face.getIoService().post([this, reason] { m_onFailure(reason); });
 }
 
+std::string
+PipelineInterests::formatThroughput(double throughput)
+{
+  int pow = 0;
+  while (throughput >= 1000.0 && pow < 4) {
+    throughput /= 1000.0;
+    pow++;
+  }
+  switch (pow) {
+    case 0:
+      return to_string(throughput) + " bit/s";
+    case 1:
+      return to_string(throughput) + " kbit/s";
+    case 2:
+      return to_string(throughput) + " Mbit/s";
+    case 3:
+      return to_string(throughput) + " Gbit/s";
+    case 4:
+      return to_string(throughput) + " Tbit/s";
+  }
+  return "";
+}
+
+void
+PipelineInterests::printSummary() const
+{
+  typedef time::duration<double, time::milliseconds::period> Milliseconds;
+  Milliseconds timeElapsed = time::steady_clock::now() - getStartTime();
+  double throughput = (8 * m_receivedSize * 1000) / timeElapsed.count();
+
+  std::cerr << "\nAll segments have been received.\n"
+            << "Time elapsed: " << timeElapsed << "\n"
+            << "Total # of segments received: " << m_nReceived << "\n"
+            << "Total size: " << static_cast<double>(m_receivedSize) / 1000 << "kB" << "\n"
+            << "Goodput: " << formatThroughput(throughput) << "\n";
+}
+
 } // namespace chunks
 } // namespace ndn
diff --git a/tools/chunks/catchunks/pipeline-interests.hpp b/tools/chunks/catchunks/pipeline-interests.hpp
index 8fe349e..d806d1b 100644
--- a/tools/chunks/catchunks/pipeline-interests.hpp
+++ b/tools/chunks/catchunks/pipeline-interests.hpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2016-2017, Regents of the University of California,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University.
@@ -25,6 +25,7 @@
  * @author Andrea Tosatto
  * @author Davide Pesavento
  * @author Weiwei Liu
+ * @author Chavoosh Ghasemi
  */
 
 #ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_HPP
@@ -104,6 +105,20 @@
   void
   onFailure(const std::string& reason);
 
+  /**
+   * @param throughput The throughput in bits/s
+   */
+  static std::string
+  formatThroughput(double throughput);
+
+  /**
+   * @brief print statistics about quality of packet delivery
+   *
+   * This method should be overriden by each pipeline (e.g. AIMD)
+   */
+  virtual void
+  printSummary() const;
+
 private:
   /**
    * @brief perform subclass-specific operations to fetch all the segments
@@ -126,6 +141,9 @@
   uint64_t m_lastSegmentNo;
   uint64_t m_excludedSegmentNo;
 
+  size_t m_receivedSize;  ///< size of received data in bytes
+  int64_t m_nReceived; ///< number of segments received
+
 PUBLIC_WITH_TESTS_ELSE_PROTECTED:
   bool m_hasFinalBlockId; ///< true if the last segment number is known