tools: add congestion marking parameters to nfdc face create

refs #4465

Change-Id: I5df5d6136f4729ad836a72f55531208d868da5f7
diff --git a/tools/nfd-status-http-server-files/nfd-status.xsl b/tools/nfd-status-http-server-files/nfd-status.xsl
index d9b8885..9620cba 100644
--- a/tools/nfd-status-http-server-files/nfd-status.xsl
+++ b/tools/nfd-status-http-server-files/nfd-status.xsl
@@ -36,7 +36,26 @@
 
 <xsl:template name="formatDuration">
   <xsl:param name="duration" />
-  <xsl:variable name="seconds"><xsl:value-of select="substring($duration, 3, string-length($duration)-3)" /></xsl:variable>
+  <xsl:variable name="milliseconds">
+    <xsl:choose>
+      <xsl:when test="contains($duration, '.')">
+        <xsl:value-of select="substring($duration, string-length($duration)-4, 3)" />
+      </xsl:when>
+      <xsl:otherwise>
+        0
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:variable name="seconds">
+    <xsl:choose>
+      <xsl:when test="contains($duration, '.')">
+        <xsl:value-of select="substring($duration, 3, string-length($duration)-7)" />
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="substring($duration, 3, string-length($duration)-3)" />
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
   <xsl:variable name="days"><xsl:value-of select="floor($seconds div 86400)" /></xsl:variable>
   <xsl:variable name="hours"><xsl:value-of select="floor($seconds div 3600)" /></xsl:variable>
   <xsl:variable name="minutes"><xsl:value-of select="floor($seconds div 60)" /></xsl:variable>
@@ -65,6 +84,12 @@
     <xsl:otherwise>
       <xsl:value-of select="$seconds"/> seconds
     </xsl:otherwise>
+    <xsl:when test="$milliseconds > 1">
+      <xsl:value-of select="$milliseconds"/> milliseconds
+    </xsl:when>
+    <xsl:when test="$milliseconds = 1">
+      <xsl:value-of select="$milliseconds"/> millisecond
+    </xsl:when>
   </xsl:choose>
 </xsl:template>
 
@@ -183,6 +208,12 @@
             <xsl:when test="nfd:flags/nfd:localFieldsEnabled">
               local-fields
             </xsl:when>
+            <xsl:when test="nfd:flags/nfd:lpReliabilityEnabled">
+              reliability
+            </xsl:when>
+            <xsl:when test="nfd:flags/nfd:congestionMarkingEnabled">
+              congestion-marking
+            </xsl:when>
           </xsl:choose>
         </td>
         <td>
diff --git a/tools/nfdc/face-module.cpp b/tools/nfdc/face-module.cpp
index 1a88229..560b223 100644
--- a/tools/nfdc/face-module.cpp
+++ b/tools/nfdc/face-module.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017,  Regents of the University of California,
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -25,7 +25,6 @@
 
 #include "face-module.hpp"
 #include "find-face.hpp"
-#include "format-helpers.hpp"
 
 namespace nfd {
 namespace tools {
@@ -54,7 +53,10 @@
     .addArg("remote", ArgValueType::FACE_URI, Required::YES, Positional::YES)
     .addArg("persistency", ArgValueType::FACE_PERSISTENCY, Required::NO, Positional::YES)
     .addArg("local", ArgValueType::FACE_URI, Required::NO, Positional::NO)
-    .addArg("reliability", ArgValueType::BOOLEAN, Required::NO, Positional::NO);
+    .addArg("reliability", ArgValueType::BOOLEAN, Required::NO, Positional::NO)
+    .addArg("congestion-marking", ArgValueType::BOOLEAN, Required::NO, Positional::NO)
+    .addArg("congestion-marking-interval", ArgValueType::UNSIGNED, Required::NO, Positional::NO)
+    .addArg("default-congestion-threshold", ArgValueType::UNSIGNED, Required::NO, Positional::NO);
   parser.addCommand(defFaceCreate, &FaceModule::create);
 
   CommandDefinition defFaceDestroy("face", "destroy");
@@ -153,6 +155,9 @@
   auto localUri = ctx.args.getOptional<FaceUri>("local");
   auto persistency = ctx.args.get<FacePersistency>("persistency", FacePersistency::FACE_PERSISTENCY_PERSISTENT);
   auto lpReliability = ctx.args.getTribool("reliability");
+  auto congestionMarking = ctx.args.getTribool("congestion-marking");
+  auto baseCongestionMarkingIntervalMs = ctx.args.getOptional<uint64_t>("congestion-marking-interval");
+  auto defaultCongestionThreshold = ctx.args.getOptional<uint64_t>("default-congestion-threshold");
 
   FaceUri canonicalRemote;
   ndn::optional<FaceUri> canonicalLocal;
@@ -168,9 +173,8 @@
             << ia("id") << resp.getFaceId()
             << ia("local") << resp.getLocalUri()
             << ia("remote") << resp.getUri()
-            << ia("persistency") << resp.getFacePersistency()
-            << ia("reliability") << (resp.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED) ? "on" : "off")
-            << '\n';
+            << ia("persistency") << resp.getFacePersistency();
+    printFaceParams(ctx.out, ia, resp);
   };
 
   auto updateFace = [&printPositiveResult] (ControlParameters respParams, ControlParameters resp) {
@@ -200,24 +204,36 @@
           ctx.makeCommandFailureHandler("upgrading face persistency"),
           ctx.makeCommandOptions());
     }
-    else if (lpReliability && !respParams.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
-      // enable reliability
+    else if ((!boost::logic::indeterminate(lpReliability) &&
+              lpReliability != respParams.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) ||
+             (!boost::logic::indeterminate(congestionMarking) &&
+              congestionMarking != respParams.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED)) ||
+             baseCongestionMarkingIntervalMs ||
+             defaultCongestionThreshold) {
       ControlParameters params;
-      params.setFaceId(respParams.getFaceId()).setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, true);
+      params.setFaceId(respParams.getFaceId());
+
+      if (!boost::logic::indeterminate(lpReliability) &&
+          lpReliability != respParams.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
+        params.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, lpReliability);
+      }
+      if (!boost::logic::indeterminate(congestionMarking) &&
+          congestionMarking != respParams.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED)) {
+        params.setFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED, congestionMarking);
+      }
+
+      if (baseCongestionMarkingIntervalMs) {
+        params.setBaseCongestionMarkingInterval(time::milliseconds(*baseCongestionMarkingIntervalMs));
+      }
+
+      if (defaultCongestionThreshold) {
+        params.setDefaultCongestionThreshold(*defaultCongestionThreshold);
+      }
+
       ctx.controller.start<ndn::nfd::FaceUpdateCommand>(
           params,
           bind(updateFace, respParams, _1),
-          ctx.makeCommandFailureHandler("enabling reliability"),
-          ctx.makeCommandOptions());
-    }
-    else if (!lpReliability && respParams.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
-      // disable reliability
-      ControlParameters params;
-      params.setFaceId(respParams.getFaceId()).setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, false);
-      ctx.controller.start<ndn::nfd::FaceUpdateCommand>(
-          params,
-          bind(updateFace, respParams, _1),
-          ctx.makeCommandFailureHandler("disabling reliability"),
+          ctx.makeCommandFailureHandler("updating face"),
           ctx.makeCommandOptions());
     }
     else {
@@ -237,6 +253,15 @@
     if (!boost::logic::indeterminate(lpReliability)) {
       params.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, lpReliability);
     }
+    if (!boost::logic::indeterminate(congestionMarking)) {
+      params.setFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED, congestionMarking);
+    }
+    if (baseCongestionMarkingIntervalMs) {
+      params.setBaseCongestionMarkingInterval(time::milliseconds(*baseCongestionMarkingIntervalMs));
+    }
+    if (defaultCongestionThreshold) {
+      params.setDefaultCongestionThreshold(*defaultCongestionThreshold);
+    }
 
     ctx.controller.start<ndn::nfd::FaceCreateCommand>(
       params,
@@ -309,9 +334,8 @@
       ctx.out << ia("id") << face.getFaceId()
               << ia("local") << face.getLocalUri()
               << ia("remote") << face.getRemoteUri()
-              << ia("persistency") << face.getFacePersistency()
-              << ia("reliability") << (resp.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED) ? "on" : "off")
-              << '\n';
+              << ia("persistency") << face.getFacePersistency();
+      printFaceParams(ctx.out, ia, resp);
     },
     ctx.makeCommandFailureHandler("destroying face"),
     ctx.makeCommandOptions());
@@ -360,6 +384,21 @@
   os << "<facePersistency>" << item.getFacePersistency() << "</facePersistency>";
   os << "<linkType>" << item.getLinkType() << "</linkType>";
 
+  if (!item.hasBaseCongestionMarkingInterval() && !item.hasDefaultCongestionThreshold()) {
+    os << "<congestion/>";
+  }
+  else {
+    os << "<congestion>";
+    if (item.hasBaseCongestionMarkingInterval()) {
+      os << "<baseMarkingInterval>" << xml::formatDuration(item.getBaseCongestionMarkingInterval())
+         << "</baseMarkingInterval>";
+    }
+    if (item.hasDefaultCongestionThreshold()) {
+      os << "<defaultThreshold>" << item.getDefaultCongestionThreshold() << "</defaultThreshold>";
+    }
+    os << "</congestion>";
+  }
+
   if (item.getFlags() == 0) {
     os << "<flags/>";
   }
@@ -371,6 +410,9 @@
     if (item.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
       os << "<lpReliabilityEnabled/>";
     }
+    if (item.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED)) {
+      os << "<congestionMarkingEnabled/>";
+    }
     os << "</flags>";
   }
 
@@ -409,14 +451,27 @@
 void
 FaceModule::formatItemText(std::ostream& os, const FaceStatus& item, bool wantMultiLine)
 {
-  text::ItemAttributes ia(wantMultiLine, 8);
+  text::ItemAttributes ia(wantMultiLine, 10);
 
   os << ia("faceid") << item.getFaceId();
   os << ia("remote") << item.getRemoteUri();
   os << ia("local") << item.getLocalUri();
 
   if (item.hasExpirationPeriod()) {
-    os << ia("expires") << text::formatDuration(item.getExpirationPeriod());
+    os << ia("expires") << text::formatDuration<time::seconds>(item.getExpirationPeriod());
+  }
+
+  if (item.hasBaseCongestionMarkingInterval() || item.hasDefaultCongestionThreshold()) {
+    os << ia("congestion") << "{";
+    text::Separator congestionSep("", " ");
+    if (item.hasBaseCongestionMarkingInterval()) {
+      os << congestionSep << "base-marking-interval="
+         << text::formatDuration<time::milliseconds>(item.getBaseCongestionMarkingInterval());
+    }
+    if (item.hasDefaultCongestionThreshold()) {
+      os << congestionSep << "default-threshold=" << item.getDefaultCongestionThreshold() << "B";
+    }
+    os << "}";
   }
 
   os << ia("counters")
@@ -442,11 +497,29 @@
   if (item.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
     os << flagSep << "lp-reliability";
   }
+  if (item.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED)) {
+    os << flagSep << "congestion-marking";
+  }
   os << '}';
 
   os << ia.end();
 }
 
+void
+FaceModule::printFaceParams(std::ostream& os, text::ItemAttributes& ia, const ControlParameters& resp)
+{
+  os << ia("reliability") << (resp.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED) ? "on" : "off")
+     << ia("congestion-marking") << (resp.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED) ? "on" : "off");
+  if (resp.hasBaseCongestionMarkingInterval()) {
+    os << ia("congestion-marking-interval")
+       << text::formatDuration<time::milliseconds>(resp.getBaseCongestionMarkingInterval());
+  }
+  if (resp.hasDefaultCongestionThreshold()) {
+    os << ia("default-congestion-threshold") << resp.getDefaultCongestionThreshold() << "B";
+  }
+  os << '\n';
+}
+
 } // namespace nfdc
 } // namespace tools
 } // namespace nfd
diff --git a/tools/nfdc/face-module.hpp b/tools/nfdc/face-module.hpp
index 6c190a1..9f0ac2a 100644
--- a/tools/nfdc/face-module.hpp
+++ b/tools/nfdc/face-module.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -28,6 +28,7 @@
 
 #include "module.hpp"
 #include "command-parser.hpp"
+#include "format-helpers.hpp"
 
 namespace nfd {
 namespace tools {
@@ -93,6 +94,14 @@
   static void
   formatItemText(std::ostream& os, const FaceStatus& item, bool wantMultiLine);
 
+  /** \brief print face response parameters to specified ostream
+   *  \param os output stream
+   *  \param ia ItemAttributes used to format output
+   *  \param resp response control parameters to print
+   */
+  static void
+  printFaceParams(std::ostream& os, text::ItemAttributes& ia, const ControlParameters& resp);
+
 private:
   std::vector<FaceStatus> m_status;
 };
diff --git a/tools/nfdc/format-helpers.cpp b/tools/nfdc/format-helpers.cpp
index 3d851dd..6d5a983 100644
--- a/tools/nfdc/format-helpers.cpp
+++ b/tools/nfdc/format-helpers.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -25,6 +25,9 @@
 
 #include "format-helpers.hpp"
 
+#include <iomanip>
+#include <sstream>
+
 namespace nfd {
 namespace tools {
 namespace nfdc {
@@ -73,9 +76,28 @@
 }
 
 std::string
-formatSeconds(time::seconds d)
+formatDuration(time::nanoseconds d)
 {
-  return "PT" + to_string(d.count()) + "S";
+  std::ostringstream str;
+
+  if (d < 0_ns) {
+    str << "-";
+  }
+
+  str << "PT";
+
+  time::seconds seconds(time::duration_cast<time::seconds>(time::abs(d)));
+  time::milliseconds ms(time::duration_cast<time::milliseconds>(time::abs(d) - seconds));
+
+  str << seconds.count();
+
+  if (ms >= 1_ms) {
+    str << "." << std::setfill('0') << std::setw(3) << ms.count();
+  }
+
+  str << "S";
+
+  return str.str();
 }
 
 std::string
@@ -156,12 +178,6 @@
 }
 
 std::string
-formatSeconds(time::seconds d, bool isLong)
-{
-  return to_string(d.count()) + (isLong ? " seconds" : "s");
-}
-
-std::string
 formatTimestamp(time::system_clock::TimePoint t)
 {
   return time::toIsoString(t);
diff --git a/tools/nfdc/format-helpers.hpp b/tools/nfdc/format-helpers.hpp
index 9283855..c0f8fd1 100644
--- a/tools/nfdc/format-helpers.hpp
+++ b/tools/nfdc/format-helpers.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -50,16 +50,17 @@
 std::ostream&
 operator<<(std::ostream& os, const Text& text);
 
+/** \return duration in XML duration format
+ *
+ *  Definition of this format: https://www.w3.org/TR/xmlschema11-2/#duration
+ */
 std::string
-formatSeconds(time::seconds d);
+formatDuration(time::nanoseconds d);
 
-template<typename DURATION>
-std::string
-formatDuration(DURATION d)
-{
-  return formatSeconds(time::duration_cast<time::seconds>(d));
-}
-
+/** \return timestamp in XML dateTime format
+ *
+ *  Definition of this format: https://www.w3.org/TR/xmlschema11-2/#dateTime
+ */
 std::string
 formatTimestamp(time::system_clock::TimePoint t);
 
@@ -164,14 +165,69 @@
 std::ostream&
 operator<<(std::ostream& os, const ItemAttributes::Attribute& attr);
 
-std::string
-formatSeconds(time::seconds d, bool isLong = false);
+namespace detail {
 
-template<typename DURATION>
+template<typename DurationT>
 std::string
-formatDuration(DURATION d, bool isLong = false)
+getTimeUnit(bool isLong);
+
+template<>
+inline std::string
+getTimeUnit<time::nanoseconds>(bool isLong)
 {
-  return formatSeconds(time::duration_cast<time::seconds>(d), isLong);
+  return isLong ? "nanoseconds" : "ns";
+}
+
+template<>
+inline std::string
+getTimeUnit<time::microseconds>(bool isLong)
+{
+  return isLong ? "microseconds" : "us";
+}
+
+template<>
+inline std::string
+getTimeUnit<time::milliseconds>(bool isLong)
+{
+  return isLong ? "milliseconds" : "ms";
+}
+
+template<>
+inline std::string
+getTimeUnit<time::seconds>(bool isLong)
+{
+  return isLong ? "seconds" : "s";
+}
+
+template<>
+inline std::string
+getTimeUnit<time::minutes>(bool isLong)
+{
+  return isLong ? "minutes" : "m";
+}
+
+template<>
+inline std::string
+getTimeUnit<time::hours>(bool isLong)
+{
+  return isLong ? "hours" : "h";
+}
+
+template<>
+inline std::string
+getTimeUnit<time::days>(bool isLong)
+{
+  return isLong ? "days" : "d";
+}
+
+} // namespace detail
+
+template<typename OutputPrecision>
+std::string
+formatDuration(time::nanoseconds d, bool isLong = false)
+{
+  return to_string(time::duration_cast<OutputPrecision>(d).count()) +
+         (isLong ? " " : "") + detail::getTimeUnit<OutputPrecision>(isLong);
 }
 
 std::string
diff --git a/tools/nfdc/forwarder-general-module.cpp b/tools/nfdc/forwarder-general-module.cpp
index e490892..4a85cdd 100644
--- a/tools/nfdc/forwarder-general-module.cpp
+++ b/tools/nfdc/forwarder-general-module.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017,  Regents of the University of California,
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -64,7 +64,8 @@
   os << "<version>" << xml::Text{item.getNfdVersion()} << "</version>";
   os << "<startTime>" << xml::formatTimestamp(item.getStartTimestamp()) << "</startTime>";
   os << "<currentTime>" << xml::formatTimestamp(item.getCurrentTimestamp()) << "</currentTime>";
-  os << "<uptime>" << xml::formatDuration(calculateUptime(item)) << "</uptime>";
+  os << "<uptime>" << xml::formatDuration(time::duration_cast<time::seconds>(calculateUptime(item)))
+     << "</uptime>";
 
   os << "<nNameTreeEntries>" << item.getNNameTreeEntries() << "</nNameTreeEntries>";
   os << "<nFibEntries>" << item.getNFibEntries() << "</nFibEntries>";
@@ -101,7 +102,7 @@
   os << "               version=" << item.getNfdVersion() << "\n";
   os << "             startTime=" << text::formatTimestamp(item.getStartTimestamp()) << "\n";
   os << "           currentTime=" << text::formatTimestamp(item.getCurrentTimestamp()) << "\n";
-  os << "                uptime=" << text::formatDuration(calculateUptime(item), true) << "\n";
+  os << "                uptime=" << text::formatDuration<time::seconds>(calculateUptime(item), true) << "\n";
 
   os << "      nNameTreeEntries=" << item.getNNameTreeEntries() << "\n";
   os << "           nFibEntries=" << item.getNFibEntries() << "\n";
diff --git a/tools/nfdc/rib-module.cpp b/tools/nfdc/rib-module.cpp
index 5ca7195..f9d888f 100644
--- a/tools/nfdc/rib-module.cpp
+++ b/tools/nfdc/rib-module.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -195,7 +195,7 @@
               << ia("cost") << resp.getCost()
               << ia("flags") << static_cast<ndn::nfd::RouteFlags>(resp.getFlags());
       if (resp.hasExpirationPeriod()) {
-        ctx.out << ia("expires") << resp.getExpirationPeriod().count() << "ms\n";
+        ctx.out << ia("expires") << text::formatDuration<time::milliseconds>(resp.getExpirationPeriod()) << "\n";
       }
       else {
         ctx.out<< ia("expires") << "never\n";
@@ -307,7 +307,7 @@
     }
     if (route.hasExpirationPeriod()) {
       os << "<expirationPeriod>"
-         << xml::formatDuration(route.getExpirationPeriod())
+         << xml::formatDuration(time::duration_cast<time::seconds>(route.getExpirationPeriod()))
          << "</expirationPeriod>";
     }
     os << "</route>";
@@ -356,7 +356,7 @@
   os << ia("cost") << route.getCost();
   os << ia("flags") << static_cast<ndn::nfd::RouteFlags>(route.getFlags());
   if (route.hasExpirationPeriod()) {
-    os << ia("expires") << text::formatDuration(route.getExpirationPeriod());
+    os << ia("expires") << text::formatDuration<time::seconds>(route.getExpirationPeriod());
   }
   else {
     os << ia("expires") << "never";