[ndnSIM] face: Emulation mode of ndn::Face

Change-Id: Ib345978575104afa436296d2f5a824a39e3a9780
diff --git a/ndn-cxx/face.cpp b/ndn-cxx/face.cpp
index 48f1095..c630302 100644
--- a/ndn-cxx/face.cpp
+++ b/ndn-cxx/face.cpp
@@ -28,6 +28,11 @@
 #include "ndn-cxx/util/scope.hpp"
 #include "ndn-cxx/util/time.hpp"
 
+#include "ns3/node-list.h"
+#include "ns3/ndnSIM/helper/ndn-stack-helper.hpp"
+#include "ns3/ndnSIM/NFD/daemon/face/generic-link-service.hpp"
+#include "ns3/ndnSIM/NFD/daemon/face/internal-transport.hpp"
+
 // NDN_LOG_INIT(ndn.Face) is declared in face-impl.hpp
 
 // A callback scheduled through io.post and io.dispatch may be invoked after the face is destructed.
@@ -36,7 +41,7 @@
 #define IO_CAPTURE_WEAK_IMPL(OP) \
   { \
     weak_ptr<Impl> implWeak(m_impl); \
-    m_ioService.OP([=] { \
+    m_impl->m_scheduler.scheduleEvent(time::seconds(0), [=] { \
       auto impl = implWeak.lock(); \
       if (impl != nullptr) {
 #define IO_CAPTURE_WEAK_IMPL_END \
@@ -57,44 +62,26 @@
 }
 
 Face::Face(shared_ptr<Transport> transport)
-  : m_internalIoService(make_unique<boost::asio::io_service>())
-  , m_ioService(*m_internalIoService)
-  , m_internalKeyChain(make_unique<KeyChain>())
 {
-  construct(std::move(transport), *m_internalKeyChain);
+  construct(std::move(transport), ns3::ndn::StackHelper::getKeyChain());
 }
 
 Face::Face(boost::asio::io_service& ioService)
-  : m_ioService(ioService)
-  , m_internalKeyChain(make_unique<KeyChain>())
 {
-  construct(nullptr, *m_internalKeyChain);
-}
-
-Face::Face(const std::string& host, const std::string& port)
-  : m_internalIoService(make_unique<boost::asio::io_service>())
-  , m_ioService(*m_internalIoService)
-  , m_internalKeyChain(make_unique<KeyChain>())
-{
-  construct(make_shared<TcpTransport>(host, port), *m_internalKeyChain);
+  construct(nullptr, ns3::ndn::StackHelper::getKeyChain());
 }
 
 Face::Face(shared_ptr<Transport> transport, KeyChain& keyChain)
-  : m_internalIoService(make_unique<boost::asio::io_service>())
-  , m_ioService(*m_internalIoService)
 {
   construct(std::move(transport), keyChain);
 }
 
 Face::Face(shared_ptr<Transport> transport, boost::asio::io_service& ioService)
-  : m_ioService(ioService)
-  , m_internalKeyChain(make_unique<KeyChain>())
 {
-  construct(std::move(transport), *m_internalKeyChain);
+  construct(transport, ns3::ndn::StackHelper::getKeyChain());
 }
 
 Face::Face(shared_ptr<Transport> transport, boost::asio::io_service& ioService, KeyChain& keyChain)
-  : m_ioService(ioService)
 {
   construct(std::move(transport), keyChain);
 }
@@ -102,43 +89,25 @@
 shared_ptr<Transport>
 Face::makeDefaultTransport()
 {
-  std::string transportUri;
+  ns3::Ptr<ns3::Node> node = ns3::NodeList::GetNode(ns3::Simulator::GetContext());
+  NS_ASSERT_MSG(node->GetObject<ns3::ndn::L3Protocol>() != 0,
+                "NDN stack should be installed on the node " << node);
 
-  const char* transportEnviron = getenv("NDN_CLIENT_TRANSPORT");
-  if (transportEnviron != nullptr) {
-    transportUri = transportEnviron;
-  }
-  else {
-    ConfigFile config;
-    transportUri = config.getParsedConfiguration().get<std::string>("transport", "");
-  }
+  auto uri = ::nfd::FaceUri("ndnFace://" + boost::lexical_cast<std::string>(node->GetId()));
 
-  if (transportUri.empty()) {
-    // transport not specified, use default Unix transport.
-    return UnixTransport::create("");
-  }
+  ::nfd::face::GenericLinkService::Options serviceOpts;
+  serviceOpts.allowLocalFields = true;
 
-  std::string protocol;
-  try {
-    FaceUri uri(transportUri);
-    protocol = uri.getScheme();
+  auto nfdFace = make_shared<::nfd::Face>(make_unique<::nfd::face::GenericLinkService>(serviceOpts),
+                                          make_unique<::nfd::face::InternalForwarderTransport>(uri, uri));
+  auto forwarderTransport = static_cast<::nfd::face::InternalForwarderTransport*>(nfdFace->getTransport());
 
-    if (protocol == "unix") {
-      return UnixTransport::create(transportUri);
-    }
-    else if (protocol == "tcp" || protocol == "tcp4" || protocol == "tcp6") {
-      return TcpTransport::create(transportUri);
-    }
-    else {
-      NDN_THROW(ConfigFile::Error("Unsupported transport protocol \"" + protocol + "\""));
-    }
-  }
-  catch (const Transport::Error&) {
-    NDN_THROW_NESTED(ConfigFile::Error("Failed to create transport"));
-  }
-  catch (const FaceUri::Error&) {
-    NDN_THROW_NESTED(ConfigFile::Error("Failed to create transport"));
-  }
+  auto clientTransport = make_shared<::nfd::face::InternalClientTransport>();
+  clientTransport->connectToForwarder(forwarderTransport);
+
+  node->GetObject<ns3::ndn::L3Protocol>()->addFace(nfdFace);;
+
+  return clientTransport;
 }
 
 void
@@ -259,32 +228,6 @@
 void
 Face::doProcessEvents(time::milliseconds timeout, bool keepThread)
 {
-  if (m_ioService.stopped()) {
-    m_ioService.reset(); // ensure that run()/poll() will do some work
-  }
-
-  auto onThrow = make_scope_fail([this] { m_impl->shutdown(); });
-
-  if (timeout < 0_ms) {
-    // do not block if timeout is negative, but process pending events
-    m_ioService.poll();
-    return;
-  }
-
-  if (timeout > 0_ms) {
-    m_impl->m_processEventsTimeoutEvent = m_impl->m_scheduler.schedule(timeout,
-      [&io = m_ioService, &work = m_impl->m_ioServiceWork] {
-        io.stop();
-        work.reset();
-      });
-  }
-
-  if (keepThread) {
-    // work will ensure that m_ioService is running until work object exists
-    m_impl->m_ioServiceWork = make_unique<boost::asio::io_service::work>(m_ioService);
-  }
-
-  m_ioService.run();
 }
 
 void
diff --git a/ndn-cxx/face.hpp b/ndn-cxx/face.hpp
index 45eaad2..3fa5209 100644
--- a/ndn-cxx/face.hpp
+++ b/ndn-cxx/face.hpp
@@ -163,15 +163,6 @@
   Face(boost::asio::io_service& ioService);
 
   /**
-   * @brief Create Face using TcpTransport
-   *
-   * @param host IP address or hostname of the NDN forwarder
-   * @param port port number or service name of the NDN forwarder (**default**: "6363")
-   */
-  explicit
-  Face(const std::string& host, const std::string& port = "6363");
-
-  /**
    * @brief Create Face using given transport and KeyChain
    * @param transport the transport for lower layer communication. If nullptr,
    *                  a default transport will be used.
@@ -417,12 +408,12 @@
   shutdown();
 
   /**
-   * @brief Returns a reference to the io_service used by this face.
+   * @return Dereferenced nullptr (cannot use IoService in simulations), preserved for API compatibility
    */
   boost::asio::io_service&
   getIoService()
   {
-    return m_ioService;
+    return *static_cast<boost::asio::io_service*>(nullptr);
   }
 
 NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PROTECTED:
@@ -456,22 +447,8 @@
   onReceiveElement(const Block& blockFromDaemon);
 
 private:
-  /// the io_service owned by this Face, may be null
-  unique_ptr<boost::asio::io_service> m_internalIoService;
-  /// the io_service used by this Face
-  boost::asio::io_service& m_ioService;
-
   shared_ptr<Transport> m_transport;
 
-  /**
-   * @brief If not null, a pointer to an internal KeyChain owned by this Face.
-   * @note If a KeyChain is supplied to constructor, this pointer will be null,
-   *       and the supplied KeyChain is passed to Face::Impl constructor;
-   *       currently Face does not keep a ref to the provided KeyChain
-   *       because it's not needed, but this may change in the future.
-   */
-  unique_ptr<KeyChain> m_internalKeyChain;
-
   class Impl;
   shared_ptr<Impl> m_impl;
 
diff --git a/ndn-cxx/impl/face-impl.hpp b/ndn-cxx/impl/face-impl.hpp
index d2e2ae8..60f88a8 100644
--- a/ndn-cxx/impl/face-impl.hpp
+++ b/ndn-cxx/impl/face-impl.hpp
@@ -31,8 +31,10 @@
 #include "ndn-cxx/lp/tags.hpp"
 #include "ndn-cxx/mgmt/nfd/command-options.hpp"
 #include "ndn-cxx/mgmt/nfd/controller.hpp"
-#include "ndn-cxx/transport/tcp-transport.hpp"
-#include "ndn-cxx/transport/unix-transport.hpp"
+#include "ndn-cxx/transport/transport.hpp"
+// #include "ndn-cxx/transport/tcp-transport.hpp"
+// #include "ndn-cxx/transport/unix-transport.hpp"
+#include "ndn-cxx/util/config-file.hpp"
 #include "ndn-cxx/util/logger.hpp"
 #include "ndn-cxx/util/scheduler.hpp"
 #include "ndn-cxx/util/signal.hpp"
@@ -63,8 +65,9 @@
     , m_scheduler(m_face.getIoService())
     , m_nfdController(m_face, keyChain)
   {
-    auto onEmptyPitOrNoRegisteredPrefixes = [this] {
-      // Without this extra "post", transport can get paused (-async_read) and then resumed
+    auto postOnEmptyPitOrNoRegisteredPrefixes = [this] {
+      m_scheduler.scheduleEvent(time::seconds(0), bind(&Impl::onEmptyPitOrNoRegisteredPrefixes, this));
+      // without this extra "post", transport can get paused (-async_read) and then resumed
       // (+async_read) from within onInterest/onData callback.  After onInterest/onData
       // finishes, there is another +async_read with the same memory block.  A few of such
       // async_read duplications can cause various effects and result in segfault.
@@ -313,9 +316,16 @@
   }
 
   void
+  onEmptyPitOrNoRegisteredPrefixes()
+  {
+    if (m_pendingInterestTable.empty() && m_registeredPrefixTable.empty()) {
+      m_face.m_transport->pause();
+    }
+  }
+
+  void
   shutdown()
   {
-    m_ioServiceWork.reset();
     m_pendingInterestTable.clear();
     m_registeredPrefixTable.clear();
   }
@@ -414,8 +424,6 @@
   detail::RecordContainer<InterestFilterRecord> m_interestFilterTable;
   detail::RecordContainer<RegisteredPrefix> m_registeredPrefixTable;
 
-  unique_ptr<boost::asio::io_service::work> m_ioServiceWork; // if thread needs to be preserved
-
   friend Face;
 };