src/contrib/pyviz.cc
author Gustavo J. A. M. Carneiro <gjc@inescporto.pt>
Thu Jan 29 16:46:39 2009 +0000 (2009-01-29)
changeset 3911 e070ed523ee9
parent 3902 2f13b3d633bf
permissions -rw-r--r--
Visualizer.set_follow_node(node) API (makes camera follow a node)
     1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
     2 /*
     3  * Copyright (c) 2008 INESC Porto
     4  *
     5  * This program is free software; you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License version 2 as
     7  * published by the Free Software Foundation;
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program; if not, write to the Free Software
    16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    17  *
    18  * Author: Gustavo Carneiro  <gjc@inescporto.pt>
    19  */
    20 
    21 #include <stdlib.h>
    22 #include "pyviz.h"
    23 #include "ns3/simulator.h"
    24 #include "ns3/config.h"
    25 #include "ns3/node-list.h"
    26 #include "ns3/wifi-net-device.h"
    27 #include "ns3/ethernet-header.h"
    28 #include "ns3/log.h"
    29 #include "ns3/ppp-header.h"
    30 
    31 #include <sstream>
    32 
    33 NS_LOG_COMPONENT_DEFINE ("PyViz");
    34 #define NUM_LAST_PACKETS 10
    35 
    36 static
    37 std::vector<std::string>
    38 PathSplit (std::string str)
    39 {
    40   std::vector<std::string> results;
    41   size_t cutAt;
    42   while ((cutAt = str.find_first_of('/')) != str.npos)
    43     {
    44       if(cutAt > 0)
    45         {
    46           results.push_back(str.substr(0,cutAt));
    47         }
    48       str = str.substr(cutAt+1);
    49     }
    50   if (str.length() > 0)
    51     {
    52       results.push_back(str);
    53     }
    54   return results;
    55 }
    56 
    57 
    58 namespace ns3 {
    59 
    60 static PyViz* g_visualizer = NULL;
    61 
    62 PyViz::PyViz ()
    63 {
    64   NS_ASSERT (g_visualizer == NULL);
    65   g_visualizer = this;
    66 
    67   Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Tx",
    68                    MakeCallback (&PyViz::TraceNetDevTxWifi, this));
    69 
    70   Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Rx",
    71                    MakeCallback (&PyViz::TraceNetDevRxWifi, this));
    72 
    73   Config::Connect ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/TxQueue/Dequeue",
    74                    MakeCallback (&PyViz::TraceNetDevTxCsma, this));
    75 
    76   Config::Connect ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/Rx",
    77                    MakeCallback (&PyViz::TraceNetDevRxCsma, this));
    78 
    79   Config::Connect ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/PromiscRx",
    80                    MakeCallback (&PyViz::TraceNetDevPromiscRxCsma, this));
    81 
    82   Config::Connect ("/NodeList/*/DeviceList/*/TxQueue/Drop",
    83                    MakeCallback (&PyViz::TraceDrop, this));
    84 
    85   Config::Connect ("/NodeList/*/$ns3::Ipv4L3Protocol/Drop",
    86                    MakeCallback (&PyViz::TraceDrop, this));
    87 
    88   Config::Connect ("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/TxQueue/Dequeue",
    89                    MakeCallback (&PyViz::TraceNetDevTxPointToPoint, this));
    90 
    91   Config::Connect ("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/Rx",
    92                    MakeCallback (&PyViz::TraceNetDevRxPointToPoint, this));
    93 }
    94 
    95 void
    96 PyViz::RegisterCsmaLikeDevice (std::string const &deviceTypeName)
    97 {
    98   TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
    99 
   100   std::ostringstream sstream;
   101   sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/TxQueue/Dequeue";
   102   Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxCsma, this));
   103 
   104   sstream.str ("");
   105   sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
   106   Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxCsma, this));
   107 
   108   sstream.str ("");
   109   sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/PromiscRx";
   110   Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevPromiscRxCsma, this));
   111 }
   112 
   113 void
   114 PyViz::RegisterWifiLikeDevice (std::string const &deviceTypeName)
   115 {
   116   TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
   117 
   118   std::ostringstream sstream;
   119   sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Tx";
   120   Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxWifi, this));
   121 
   122   sstream.str ("");
   123   sstream <<"/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
   124   Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxWifi, this));
   125 }
   126 
   127 void
   128 PyViz::RegisterPointToPointLikeDevice (std::string const &deviceTypeName)
   129 {
   130   TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
   131 
   132   std::ostringstream sstream;
   133   sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/TxQueue/Dequeue";
   134   Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxPointToPoint, this));
   135 
   136   sstream.str ("");
   137   sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
   138   Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxPointToPoint, this));
   139 }
   140 
   141 void
   142 PyViz::SetPacketCaptureOptions (uint32_t nodeId, PacketCaptureOptions options)
   143 {
   144   NS_LOG_DEBUG ("  SetPacketCaptureOptions " << nodeId
   145                 << " PacketCaptureOptions (headers size = " << options.headers.size ()
   146                 << " mode = " << options.mode << " numLastPackets = " << options.numLastPackets
   147                 << ")");
   148   m_packetCaptureOptions[nodeId] = options;
   149 }
   150 
   151 void
   152 PyViz::RegisterDropTracePath (std::string const &tracePath)
   153 {
   154   Config::Connect (tracePath, MakeCallback (&PyViz::TraceDrop, this));
   155 }
   156 
   157 PyViz::~PyViz ()
   158 {
   159   NS_ASSERT (g_visualizer == this);
   160   g_visualizer = NULL;
   161 }
   162 
   163 void PyViz::DoPause (std::string const &message)
   164 {
   165   m_pauseMessages.push_back (message);
   166   m_stop = true;
   167   NS_LOG_LOGIC (Simulator::Now ().GetSeconds () << ": Have "
   168                 << g_visualizer->m_pauseMessages.size () << " pause messages");
   169 }
   170 
   171 void PyViz::Pause (std::string const &message)
   172 {
   173   NS_ASSERT (g_visualizer);
   174   g_visualizer->DoPause (message);
   175 }
   176 
   177 std::vector<std::string>
   178 PyViz::GetPauseMessages () const
   179 {
   180   NS_LOG_LOGIC (Simulator::Now ().GetSeconds () << ": GetPauseMessages: have "
   181                 << g_visualizer->m_pauseMessages.size () << " pause messages");
   182   return m_pauseMessages;
   183 }
   184     
   185 
   186 void
   187 PyViz::CallbackStopSimulation ()
   188 {
   189   NS_LOG_FUNCTION_NOARGS ();
   190   m_stop = true;
   191 }
   192 
   193 void
   194 PyViz::SimulatorRunUntil (Time time)
   195 {
   196   NS_LOG_LOGIC ("SimulatorRunUntil " << time << " (now is " << Simulator::Now () << ")");
   197 
   198   m_pauseMessages.clear ();
   199   m_transmissionSamples.clear ();
   200   m_packetDrops.clear ();
   201 
   202   Time expirationTime = Simulator::Now () - Seconds (10);
   203 
   204   // Clear very old transmission records
   205   for (std::map<TxRecordKey, TxRecordValue>::iterator iter = m_txRecords.begin ();
   206        iter != m_txRecords.end ();)
   207     {
   208       if (iter->second.time < expirationTime)
   209         {
   210           m_txRecords.erase (iter++);
   211         }
   212       else
   213         {
   214           iter++;
   215         }
   216     }
   217 
   218   // Clear very old packets of interest
   219   for (std::map<uint32_t, Time>::iterator iter = m_packetsOfInterest.begin ();
   220        iter != m_packetsOfInterest.end ();)
   221     {
   222       if (iter->second < expirationTime)
   223         {
   224           m_packetsOfInterest.erase (iter++);
   225         }
   226       else
   227         {
   228           iter++;
   229         }
   230     }
   231 
   232   if (Simulator::IsFinished () || Simulator::Now () >= time)
   233     {
   234       return;
   235     }
   236   // Schedule a dummy callback function for the target time, to make
   237   // sure we stop at the right time.  Otherwise, simulations with few
   238   // events just appear to "jump" big chunks of time.
   239   NS_LOG_LOGIC ("Schedule dummy callback to be called in " << (time - Simulator::Now ()));
   240   m_stop = false;
   241   Simulator::Cancel (m_stopCallbackEvent);
   242   m_stopCallbackEvent = Simulator::Schedule (time - Simulator::Now (), &PyViz::CallbackStopSimulation, this);
   243 
   244   while (!Simulator::IsFinished ())
   245     {
   246       if (m_stop)
   247         {
   248           break;
   249         }
   250       Simulator::RunOneEvent ();
   251     }
   252 }
   253 
   254 bool PyViz::TransmissionSampleKey::operator < (PyViz::TransmissionSampleKey const &other) const
   255 {
   256   if (this->transmitter < other.transmitter)
   257     {
   258       return true;
   259     }
   260   if (this->transmitter != other.transmitter)
   261     {
   262       return false;
   263     }
   264   if (this->receiver < other.receiver)
   265     {
   266       return true;}
   267   if (this->receiver != other.receiver)
   268     {
   269       return false;
   270     }
   271   if (this->channel < other.channel)
   272     {
   273       return true;
   274     }
   275   else
   276     {
   277       return false;
   278     }
   279 }
   280 
   281 bool PyViz::TransmissionSampleKey::operator == (PyViz::TransmissionSampleKey const &other) const
   282 {
   283   bool retval = (transmitter == other.transmitter) &&
   284     (receiver == other.receiver) &&
   285     (channel == other.channel);
   286   return retval;
   287 }
   288 
   289 
   290 PyViz::NetDeviceStatistics &
   291 PyViz::FindNetDeviceStatistics (int node, int interface)
   292 {
   293   std::map<uint32_t, std::vector<NetDeviceStatistics> >::iterator nodeStatsIter = m_nodesStatistics.find (node);
   294   std::vector<NetDeviceStatistics> *stats;
   295   if (nodeStatsIter == m_nodesStatistics.end ())
   296     {
   297       stats = &m_nodesStatistics[node];
   298       stats->resize (NodeList::GetNode (node)->GetNDevices ());
   299     }
   300   else
   301     {
   302       stats = &(nodeStatsIter->second);
   303     }
   304   NetDeviceStatistics &devStats = (*stats)[interface];
   305   return devStats;
   306 }
   307 
   308 bool PyViz::GetPacketCaptureOptions (uint32_t nodeId, const PacketCaptureOptions **outOptions) const
   309 {
   310   std::map<uint32_t, PacketCaptureOptions>::const_iterator iter = m_packetCaptureOptions.find (nodeId);
   311   if (iter == m_packetCaptureOptions.end ())
   312     {
   313       return false;
   314     }
   315   else
   316     {
   317       *outOptions = &iter->second;
   318       return true;
   319     }
   320 }
   321 
   322 bool PyViz::FilterPacket (Ptr<const Packet> packet, const PacketCaptureOptions &options)
   323 {
   324   switch (options.mode)
   325     {
   326     case PACKET_CAPTURE_DISABLED:
   327       return false;
   328 
   329     case PACKET_CAPTURE_FILTER_HEADERS_OR:
   330       {
   331         PacketMetadata::ItemIterator metadataIterator = packet->BeginItem ();
   332         while (metadataIterator.HasNext ())
   333           {
   334             PacketMetadata::Item item = metadataIterator.Next ();
   335             if (options.headers.find (item.tid) != options.headers.end ())
   336               {
   337                 return true;
   338               }
   339           }
   340         return false;
   341       }
   342 
   343     case PACKET_CAPTURE_FILTER_HEADERS_AND:
   344       {
   345         std::set<TypeId> missingHeaders (options.headers);
   346         PacketMetadata::ItemIterator metadataIterator = packet->BeginItem ();
   347         while (metadataIterator.HasNext ())
   348           {
   349             PacketMetadata::Item item = metadataIterator.Next ();
   350             std::set<TypeId>::iterator missingIter = missingHeaders.find (item.tid);
   351             if (missingIter != missingHeaders.end ())
   352               {
   353                 missingHeaders.erase (missingIter);
   354               }
   355           }
   356         if (missingHeaders.size () == 0)
   357           {
   358             return true;
   359           }
   360         else
   361           {
   362             return false;
   363           }
   364       }
   365 
   366     default:
   367       NS_FATAL_ERROR ("should not be reached");
   368       return false;
   369     }
   370 }
   371 
   372 void PyViz::TraceDrop (std::string context, Ptr<const Packet> packet)
   373 {
   374   NS_LOG_FUNCTION (context << packet->GetUid ());
   375   std::vector<std::string> splitPath = PathSplit (context);
   376   int nodeIndex = atoi (splitPath[1].c_str ());
   377   Ptr<Node> node = NodeList::GetNode (nodeIndex);
   378 
   379   if (m_nodesOfInterest.find (nodeIndex) == m_nodesOfInterest.end ())
   380     {
   381       // if the transmitting node is not "of interest", we still
   382       // record the transmission if it is a packet of interest.
   383       if (m_packetsOfInterest.find (packet->GetUid ()) == m_packetsOfInterest.end ())
   384         {
   385           NS_LOG_DEBUG ("Packet " << packet->GetUid () << " is not of interest");
   386           return;
   387         }
   388     }
   389 
   390   // ---- "last packets"
   391   const PacketCaptureOptions *captureOptions;
   392   if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
   393     {
   394       LastPacketsSample &last = m_lastPackets[nodeIndex];
   395       PacketSample lastPacket;
   396       lastPacket.time = Simulator::Now ();
   397       lastPacket.packet = packet->Copy ();
   398       lastPacket.device = NULL;
   399       last.lastDroppedPackets.push_back (lastPacket);
   400       while (last.lastDroppedPackets.size () > captureOptions->numLastPackets)
   401         {
   402           last.lastDroppedPackets.erase (last.lastDroppedPackets.begin ());
   403         }
   404     }
   405 
   406   std::map<Ptr<Node>, uint32_t>::iterator iter = m_packetDrops.find (node);
   407   if (iter == m_packetDrops.end ())
   408     {
   409       m_packetDrops[node] = packet->GetSize ();
   410     }
   411   else
   412     {
   413       iter->second += packet->GetSize ();
   414     }
   415 }
   416 
   417 void
   418 PyViz::TraceNetDevTxWifi (std::string context, Ptr<const Packet> packet, Mac48Address destinationAddress)
   419 {
   420   NS_LOG_FUNCTION (context << packet->GetUid () << *packet);
   421 
   422   std::vector<std::string> splitPath = PathSplit (context);
   423   int nodeIndex = atoi (splitPath[1].c_str ());
   424   int devIndex = atoi (splitPath[3].c_str ());
   425   Ptr<Node> node = NodeList::GetNode (nodeIndex);
   426   Ptr<NetDevice> device = node->GetDevice (devIndex);
   427 
   428   // ---- statistics
   429   NetDeviceStatistics &stats = FindNetDeviceStatistics (nodeIndex, devIndex);
   430   ++stats.transmittedPackets;
   431   stats.transmittedBytes += packet->GetSize ();
   432 
   433   // ---- "last packets"
   434   const PacketCaptureOptions *captureOptions;
   435   if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
   436     {
   437       LastPacketsSample &last = m_lastPackets[nodeIndex];
   438       TxPacketSample lastPacket;
   439       lastPacket.time = Simulator::Now ();
   440       lastPacket.packet = packet->Copy ();
   441       lastPacket.device = device;
   442       lastPacket.to = destinationAddress;
   443       last.lastTransmittedPackets.push_back (lastPacket);
   444       while (last.lastTransmittedPackets.size () > captureOptions->numLastPackets)
   445         {
   446           last.lastTransmittedPackets.erase (last.lastTransmittedPackets.begin ());
   447         }
   448     }
   449 
   450   // ---- transmissions records
   451 
   452   if (m_nodesOfInterest.find (nodeIndex) == m_nodesOfInterest.end ())
   453     {
   454       // if the transmitting node is not "of interest", we still
   455       // record the transmission if it is a packet of interest.
   456       if (m_packetsOfInterest.find (packet->GetUid ()) == m_packetsOfInterest.end ())
   457         {
   458           NS_LOG_DEBUG ("Packet " << packet->GetUid () << " is not of interest");
   459           return;
   460         }
   461     }
   462   else
   463     {
   464       // We will follow this packet throughout the network.
   465       m_packetsOfInterest[packet->GetUid ()] = Simulator::Now ();
   466     }
   467 
   468   TxRecordValue record = { Simulator::Now (), node, false };
   469   if (destinationAddress == device->GetBroadcast ())
   470     {
   471       record.isBroadcast = true;
   472     }
   473   m_txRecords[TxRecordKey (device->GetChannel (), packet->GetUid ())] = record;
   474 }
   475 
   476 void
   477 PyViz::TraceNetDevRxCsma (std::string context, Ptr<const Packet> packet)
   478 {
   479   Ptr<Packet> p = packet->Copy ();
   480   EthernetHeader ethernetHeader;
   481   p->RemoveHeader (ethernetHeader);
   482   TraceNetDevRxWifi (context, p, ethernetHeader.GetSource ());
   483 }
   484 
   485 void
   486 PyViz::TraceNetDevRxPointToPoint (std::string context, Ptr<const Packet> packet)
   487 {
   488   Ptr<Packet> p = packet->Copy ();
   489   PppHeader h;
   490   p->RemoveHeader (h);
   491   TraceNetDevRxWifi (context, p, Mac48Address ());
   492 }
   493 
   494 void
   495 PyViz::TraceNetDevPromiscRxCsma (std::string context, Ptr<const Packet> packet, NetDevice::PacketType packetType)
   496 {
   497   Ptr<Packet> p = packet->Copy ();
   498   EthernetHeader ethernetHeader;
   499   p->RemoveHeader (ethernetHeader);
   500   // Other packet types are already being received by
   501   // TraceNetDevRxCsma; we don't want to receive them twice.
   502   if (packetType == NetDevice::PACKET_OTHERHOST)
   503     {
   504       TraceNetDevRxWifi (context, p, ethernetHeader.GetDestination ());
   505     }
   506 }
   507 
   508 void
   509 PyViz::TraceNetDevTxCsma (std::string context, Ptr<const Packet> packet)
   510 {
   511   EthernetHeader ethernetHeader;
   512   Ptr<Packet> p = packet->Copy ();
   513   p->RemoveHeader (ethernetHeader);
   514   TraceNetDevTxWifi (context, p, ethernetHeader.GetDestination ());
   515 }
   516 
   517 void
   518 PyViz::TraceNetDevTxPointToPoint (std::string context, Ptr<const Packet> packet)
   519 {
   520   Ptr<Packet> p = packet->Copy ();
   521   PppHeader h;
   522   p->RemoveHeader (h);
   523   TraceNetDevTxWifi (context, p, Mac48Address ());
   524 }
   525 
   526 void
   527 PyViz::TraceNetDevRxWifi (std::string context, Ptr<const Packet> packet, Mac48Address from)
   528 {
   529   NS_LOG_FUNCTION (context << packet->GetUid ());
   530   std::vector<std::string> splitPath = PathSplit (context);
   531   int nodeIndex = atoi (splitPath[1].c_str ());
   532   int devIndex = atoi (splitPath[3].c_str ());
   533 
   534   // ---- statistics
   535   NetDeviceStatistics &stats = FindNetDeviceStatistics (nodeIndex, devIndex);
   536   ++stats.receivedPackets;
   537   stats.receivedBytes += packet->GetSize ();
   538 
   539   Ptr<Node> node = NodeList::GetNode (nodeIndex);
   540   Ptr<NetDevice> device = node->GetDevice (devIndex);
   541 
   542   // ---- "last packets"
   543   const PacketCaptureOptions *captureOptions;
   544   if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
   545     {
   546       LastPacketsSample &last = m_lastPackets[nodeIndex];
   547       RxPacketSample lastPacket;
   548       lastPacket.time = Simulator::Now ();
   549       lastPacket.packet = packet->Copy ();
   550       lastPacket.device = device;
   551       lastPacket.from = from;
   552       last.lastReceivedPackets.push_back (lastPacket);
   553       while (last.lastReceivedPackets.size () > captureOptions->numLastPackets)
   554         {
   555           last.lastReceivedPackets.erase (last.lastReceivedPackets.begin ());
   556         }
   557     }
   558 
   559   // ---- transmissions
   560   if (m_packetsOfInterest.find (packet->GetUid ()) == m_packetsOfInterest.end ())
   561     {
   562       NS_LOG_DEBUG ("RX Packet " << packet->GetUid () << " is not of interest");
   563       return;
   564     }
   565 
   566   Ptr<Channel> channel = device->GetChannel ();
   567 
   568   std::map<TxRecordKey, TxRecordValue>::iterator recordIter = 
   569     m_txRecords.find (TxRecordKey (channel, packet->GetUid ()));
   570 
   571   if (recordIter == m_txRecords.end ())
   572     {
   573       NS_LOG_DEBUG ("RX Packet " << packet->GetUid () << " was not transmitted?!");
   574       return;
   575     }
   576   
   577   TxRecordValue &record = recordIter->second;
   578   
   579   if (record.srcNode == node)
   580     {
   581       NS_LOG_WARN ("Node " << node->GetId () << " receiving back the same packet (UID=" << packet->GetUid ()
   582                    << ") it had previously transmitted, on the same channel!");
   583       return;
   584     }
   585 
   586   TransmissionSampleKey key = { record.srcNode, node, channel };
   587 
   588 #ifdef  NS3_LOG_ENABLE
   589   NS_LOG_DEBUG("m_transmissionSamples begin:");
   590   if (g_log.IsEnabled (ns3::LOG_DEBUG))
   591     {
   592       for (std::map<TransmissionSampleKey,TransmissionSampleValue>::const_iterator iter
   593              = m_transmissionSamples.begin (); iter != m_transmissionSamples.end (); iter++)
   594         {
   595           NS_LOG_DEBUG(iter->first.transmitter<<"/"<<iter->first.transmitter->GetId () << ", "
   596                        << iter->first.receiver<<"/"<<iter->first.receiver->GetId ()
   597                        << ", " << iter->first.channel << " => " << iter->second.bytes << " (@ " << &iter->second << ")");
   598         }
   599     }
   600   NS_LOG_DEBUG("m_transmissionSamples end.");
   601 #endif
   602 
   603   std::map<TransmissionSampleKey,TransmissionSampleValue>::iterator
   604     iter = m_transmissionSamples.find (key);
   605 
   606   if (iter == m_transmissionSamples.end ())
   607     {
   608       TransmissionSampleValue sample = { packet->GetSize () };
   609       NS_LOG_DEBUG ("RX: from " << key.transmitter<<"/"<<key.transmitter->GetId() << " to "
   610                     << key.receiver<<"/"<<key.receiver->GetId()
   611                     << " channel " << channel << ": " << packet->GetSize ()
   612                     << " bytes more. => new sample with " << packet->GetSize () << " bytes.");
   613       m_transmissionSamples[key] = sample;
   614     }
   615   else
   616     {
   617       TransmissionSampleValue &sample = iter->second;
   618       NS_LOG_DEBUG ("RX: from " << key.transmitter<<"/"<<key.transmitter->GetId() << " to "
   619                     << key.receiver<<"/"<<key.receiver->GetId()
   620                     << " channel " << channel << ": " << packet->GetSize ()
   621                     << " bytes more. => sample " << &sample << " with bytes " << sample.bytes);
   622 
   623       sample.bytes += packet->GetSize ();
   624     }
   625 }
   626 
   627 PyViz::TransmissionSampleList
   628 PyViz::GetTransmissionSamples () const
   629 {
   630   NS_LOG_DEBUG ("GetTransmissionSamples BEGIN");
   631   TransmissionSampleList list;
   632   for (std::map<TransmissionSampleKey, TransmissionSampleValue>::const_iterator
   633          iter = m_transmissionSamples.begin ();
   634        iter !=  m_transmissionSamples.end ();
   635        iter++)
   636     {
   637       TransmissionSample sample;
   638       sample.transmitter = iter->first.transmitter;
   639       sample.receiver = iter->first.receiver;
   640       sample.channel = iter->first.channel;
   641       sample.bytes = iter->second.bytes;
   642       NS_LOG_DEBUG ("from " << sample.transmitter->GetId() << " to " << sample.receiver->GetId()
   643                     << ": " << sample.bytes << " bytes.");
   644       list.push_back (sample);
   645     }
   646   NS_LOG_DEBUG ("GetTransmissionSamples END");
   647   return list;
   648 }
   649 
   650 PyViz::PacketDropSampleList
   651 PyViz::GetPacketDropSamples () const
   652 {
   653   NS_LOG_DEBUG ("GetPacketDropSamples BEGIN");
   654   PacketDropSampleList list;
   655   for (std::map<Ptr<Node>, uint32_t>::const_iterator
   656          iter = m_packetDrops.begin ();
   657        iter !=  m_packetDrops.end ();
   658        iter++)
   659     {
   660       PacketDropSample sample;
   661       sample.transmitter = iter->first;
   662       sample.bytes = iter->second;
   663       NS_LOG_DEBUG ("in " << sample.transmitter->GetId() 
   664                     << ": " << sample.bytes << " bytes dropped.");
   665       list.push_back (sample);
   666     }
   667   NS_LOG_DEBUG ("GetPacketDropSamples END");
   668   return list;
   669 }
   670 
   671 void
   672 PyViz::SetNodesOfInterest (std::set<uint32_t> nodes)
   673 {
   674   m_nodesOfInterest = nodes;
   675 }
   676 
   677 std::vector<PyViz::NodeStatistics>
   678 PyViz::GetNodesStatistics () const
   679 {
   680   std::vector<PyViz::NodeStatistics> retval;
   681   for (std::map<uint32_t, std::vector<NetDeviceStatistics> >::const_iterator iter = m_nodesStatistics.begin ();
   682        iter != m_nodesStatistics.end (); iter++)
   683     {
   684       NodeStatistics stats = { iter->first, iter->second };
   685       retval.push_back (stats);
   686     }
   687   return retval;
   688 }
   689 
   690 
   691 PyViz::LastPacketsSample
   692 PyViz::GetLastPackets (uint32_t nodeId) const
   693 {
   694   NS_LOG_DEBUG ("GetLastPackets: " << nodeId);
   695   
   696   std::map<uint32_t, LastPacketsSample>::const_iterator
   697     iter = m_lastPackets.find(nodeId);
   698   if (iter != m_lastPackets.end ())
   699     {
   700       return iter->second;
   701     }
   702   else
   703     {
   704       return LastPacketsSample ();
   705     }
   706 }
   707 
   708 
   709 
   710 
   711 
   712 
   713 namespace
   714 {
   715   // Adapted from http://en.wikipedia.org/w/index.php?title=Line_clipping&oldid=248609574
   716   class FastClipping
   717   {
   718   public:
   719     struct Vector2 
   720     {
   721       double x;
   722       double y;
   723     };
   724       
   725     Vector2 m_clipMin, m_clipMax;
   726 
   727     struct Line
   728     {
   729       Vector2 start, end;
   730       double dx, dy;
   731     };
   732 
   733   private:
   734 
   735     void ClipStartTop (Line &line)
   736     {
   737       line.start.x += line.dx * (m_clipMin.y - line.start.y) / line.dy;
   738       line.start.y = m_clipMin.y;
   739     }
   740  
   741     void ClipStartBottom (Line &line)
   742     {
   743       line.start.x += line.dx * (m_clipMax.y - line.start.y) / line.dy;
   744       line.start.y = m_clipMax.y;
   745     }
   746  
   747     void ClipStartRight (Line &line)
   748     {
   749       line.start.y += line.dy * (m_clipMax.x - line.start.x) / line.dx;
   750       line.start.x = m_clipMax.x;
   751     }
   752  
   753     void ClipStartLeft (Line &line)
   754     {
   755       line.start.y += line.dy * (m_clipMin.x - line.start.x) / line.dx;
   756       line.start.x = m_clipMin.x;
   757     }
   758  
   759     void ClipEndTop (Line &line)
   760     {
   761       line.end.x += line.dx * (m_clipMin.y - line.end.y) / line.dy;
   762       line.end.y = m_clipMin.y;
   763     }
   764     
   765     void ClipEndBottom (Line &line) {
   766       line.end.x += line.dx * (m_clipMax.y - line.end.y) / line.dy;
   767       line.end.y = m_clipMax.y;
   768     }
   769  
   770     void ClipEndRight (Line &line)
   771     {
   772       line.end.y += line.dy * (m_clipMax.x - line.end.x) / line.dx;
   773       line.end.x = m_clipMax.x;
   774     }
   775  
   776     void ClipEndLeft (Line &line)
   777     {
   778       line.end.y += line.dy * (m_clipMin.x - line.end.x) / line.dx;
   779       line.end.x = m_clipMin.x;
   780     }
   781 
   782   public:
   783     FastClipping (Vector2 clipMin, Vector2 clipMax)
   784       : m_clipMin (clipMin), m_clipMax (clipMax)
   785     {
   786     }
   787     
   788  
   789     bool ClipLine (Line &line)
   790     {
   791       uint8_t lineCode = 0;
   792  
   793       if (line.end.y < m_clipMin.y)
   794         lineCode |= 8;
   795       else if (line.end.y > m_clipMax.y)
   796         lineCode |= 4;
   797  
   798       if (line.end.x > m_clipMax.x)
   799         lineCode |= 2;
   800       else if (line.end.x < m_clipMin.x)
   801         lineCode |= 1;
   802  
   803       if (line.start.y < m_clipMin.y)
   804         lineCode |= 128;
   805       else if (line.start.y > m_clipMax.y)
   806         lineCode |= 64;
   807  
   808       if (line.start.x > m_clipMax.x)
   809         lineCode |= 32;
   810       else if (line.start.x < m_clipMin.x)
   811         lineCode |= 16;
   812  
   813       // 9 - 8 - A
   814       // |   |   |
   815       // 1 - 0 - 2
   816       // |   |   |
   817       // 5 - 4 - 6
   818       switch (lineCode)
   819         {
   820           // center
   821         case 0x00:
   822           return true;
   823  
   824         case 0x01:
   825           ClipEndLeft (line);
   826           return true;
   827  
   828         case 0x02:
   829           ClipEndRight (line);
   830           return true;
   831  
   832         case 0x04:
   833           ClipEndBottom (line);
   834           return true;
   835  
   836         case 0x05:
   837           ClipEndLeft (line);
   838           if (line.end.y > m_clipMax.y)
   839             ClipEndBottom (line);
   840           return true;
   841  
   842         case 0x06:
   843           ClipEndRight (line);
   844           if (line.end.y > m_clipMax.y)
   845             ClipEndBottom (line);
   846           return true;
   847  
   848         case 0x08:
   849           ClipEndTop (line);
   850           return true;
   851  
   852         case 0x09:
   853           ClipEndLeft (line);
   854           if (line.end.y < m_clipMin.y)
   855             ClipEndTop (line);
   856           return true;
   857  
   858         case 0x0A:
   859           ClipEndRight (line);
   860           if (line.end.y < m_clipMin.y)
   861             ClipEndTop (line);
   862           return true;
   863  
   864           // left
   865         case 0x10:
   866           ClipStartLeft (line);
   867           return true;
   868  
   869         case 0x12:
   870           ClipStartLeft (line);
   871           ClipEndRight (line);
   872           return true;
   873  
   874         case 0x14:
   875           ClipStartLeft (line);
   876           if (line.start.y > m_clipMax.y)
   877             return false;
   878           ClipEndBottom (line);
   879           return true;
   880  
   881         case 0x16:
   882           ClipStartLeft (line);
   883           if (line.start.y > m_clipMax.y)
   884             return false;
   885           ClipEndBottom (line);
   886           if (line.end.x > m_clipMax.x)
   887             ClipEndRight (line);
   888           return true;
   889  
   890         case 0x18:
   891           ClipStartLeft (line);
   892           if (line.start.y < m_clipMin.y)
   893             return false;
   894           ClipEndTop (line);
   895           return true;
   896  
   897         case 0x1A:
   898           ClipStartLeft (line);
   899           if (line.start.y < m_clipMin.y)
   900             return false;
   901           ClipEndTop (line);
   902           if (line.end.x > m_clipMax.x)
   903             ClipEndRight (line);
   904           return true;
   905  
   906           // right
   907         case 0x20:
   908           ClipStartRight (line);
   909           return true;
   910  
   911         case 0x21:
   912           ClipStartRight (line);
   913           ClipEndLeft (line);
   914           return true;
   915  
   916         case 0x24:
   917           ClipStartRight (line);
   918           if (line.start.y > m_clipMax.y)
   919             return false;
   920           ClipEndBottom (line);
   921           return true;
   922  
   923         case 0x25:
   924           ClipStartRight (line);
   925           if (line.start.y > m_clipMax.y)
   926             return false;
   927           ClipEndBottom (line);
   928           if (line.end.x < m_clipMin.x)
   929             ClipEndLeft (line);
   930           return true;
   931  
   932         case 0x28:
   933           ClipStartRight (line);
   934           if (line.start.y < m_clipMin.y)
   935             return false;
   936           ClipEndTop (line);
   937           return true;
   938  
   939         case 0x29:
   940           ClipStartRight (line);
   941           if (line.start.y < m_clipMin.y)
   942             return false;
   943           ClipEndTop (line);
   944           if (line.end.x < m_clipMin.x)
   945             ClipEndLeft (line);
   946           return true;
   947  
   948           // bottom
   949         case 0x40:
   950           ClipStartBottom (line);
   951           return true;
   952  
   953         case 0x41:
   954           ClipStartBottom (line);
   955           if (line.start.x < m_clipMin.x)
   956             return false;
   957           ClipEndLeft (line);
   958           if (line.end.y > m_clipMax.y)
   959             ClipEndBottom (line);
   960           return true;
   961  
   962         case 0x42:
   963           ClipStartBottom (line);
   964           if (line.start.x > m_clipMax.x)
   965             return false;
   966           ClipEndRight (line);
   967           return true;
   968  
   969         case 0x48:
   970           ClipStartBottom (line);
   971           ClipEndTop (line);
   972           return true;
   973  
   974         case 0x49:
   975           ClipStartBottom (line);
   976           if (line.start.x < m_clipMin.x)
   977             return false;
   978           ClipEndLeft (line);
   979           if (line.end.y < m_clipMin.y)
   980             ClipEndTop (line);
   981           return true;
   982  
   983         case 0x4A:
   984           ClipStartBottom (line);
   985           if (line.start.x > m_clipMax.x)
   986             return false;
   987           ClipEndRight (line);
   988           if (line.end.y < m_clipMin.y)
   989             ClipEndTop (line);
   990           return true;
   991  
   992           // bottom-left
   993         case 0x50:
   994           ClipStartLeft (line);
   995           if (line.start.y > m_clipMax.y)
   996             ClipStartBottom (line);
   997           return true;
   998  
   999         case 0x52:
  1000           ClipEndRight (line);
  1001           if (line.end.y > m_clipMax.y)
  1002             return false;
  1003           ClipStartBottom (line);
  1004           if (line.start.x < m_clipMin.x)
  1005             ClipStartLeft (line);
  1006           return true;
  1007  
  1008         case 0x58:
  1009           ClipEndTop (line);
  1010           if (line.end.x < m_clipMin.x)
  1011             return false;
  1012           ClipStartBottom (line);
  1013           if (line.start.x < m_clipMin.x)
  1014             ClipStartLeft (line);
  1015           return true;
  1016  
  1017         case 0x5A:
  1018           ClipStartLeft (line);
  1019           if (line.start.y < m_clipMin.y)
  1020             return false;
  1021           ClipEndRight (line);
  1022           if (line.end.y > m_clipMax.y)
  1023             return false;
  1024           if (line.start.y > m_clipMax.y)
  1025             ClipStartBottom (line);
  1026           if (line.end.y < m_clipMin.y)
  1027             ClipEndTop (line);
  1028           return true;
  1029  
  1030           // bottom-right
  1031         case 0x60:
  1032           ClipStartRight (line);
  1033           if (line.start.y > m_clipMax.y)
  1034             ClipStartBottom (line);
  1035           return true;
  1036  
  1037         case 0x61:
  1038           ClipEndLeft (line);
  1039           if (line.end.y > m_clipMax.y)
  1040             return false;
  1041           ClipStartBottom (line);
  1042           if (line.start.x > m_clipMax.x)
  1043             ClipStartRight (line);
  1044           return true;
  1045  
  1046         case 0x68:
  1047           ClipEndTop (line);
  1048           if (line.end.x > m_clipMax.x)
  1049             return false;
  1050           ClipStartRight (line);
  1051           if (line.start.y > m_clipMax.y)
  1052             ClipStartBottom (line);
  1053           return true;
  1054  
  1055         case 0x69:
  1056           ClipEndLeft (line);
  1057           if (line.end.y > m_clipMax.y)
  1058             return false;
  1059           ClipStartRight (line);
  1060           if (line.start.y < m_clipMin.y)
  1061             return false;
  1062           if (line.end.y < m_clipMin.y)
  1063             ClipEndTop (line);
  1064           if (line.start.y > m_clipMax.y)
  1065             ClipStartBottom (line);
  1066           return true;
  1067  
  1068           // top
  1069         case 0x80:
  1070           ClipStartTop (line);
  1071           return true;
  1072  
  1073         case 0x81:
  1074           ClipStartTop (line);
  1075           if (line.start.x < m_clipMin.x)
  1076             return false;
  1077           ClipEndLeft (line);
  1078           return true;
  1079  
  1080         case 0x82:
  1081           ClipStartTop (line);
  1082           if (line.start.x > m_clipMax.x)
  1083             return false;
  1084           ClipEndRight (line);
  1085           return true;
  1086  
  1087         case 0x84:
  1088           ClipStartTop (line);
  1089           ClipEndBottom (line);
  1090           return true;
  1091  
  1092         case 0x85:
  1093           ClipStartTop (line);
  1094           if (line.start.x < m_clipMin.x)
  1095             return false;
  1096           ClipEndLeft (line);
  1097           if (line.end.y > m_clipMax.y)
  1098             ClipEndBottom (line);
  1099           return true;
  1100  
  1101         case 0x86:
  1102           ClipStartTop (line);
  1103           if (line.start.x > m_clipMax.x)
  1104             return false;
  1105           ClipEndRight (line);
  1106           if (line.end.y > m_clipMax.y)
  1107             ClipEndBottom (line);
  1108           return true;
  1109  
  1110           // top-left
  1111         case 0x90:
  1112           ClipStartLeft (line);
  1113           if (line.start.y < m_clipMin.y)
  1114             ClipStartTop (line);
  1115           return true;
  1116  
  1117         case 0x92:
  1118           ClipEndRight (line);
  1119           if (line.end.y < m_clipMin.y)
  1120             return false;
  1121           ClipStartTop (line);
  1122           if (line.start.x < m_clipMin.x)
  1123             ClipStartLeft (line);
  1124           return true;
  1125  
  1126         case 0x94:
  1127           ClipEndBottom (line);
  1128           if (line.end.x < m_clipMin.x)
  1129             return false;
  1130           ClipStartLeft (line);
  1131           if (line.start.y < m_clipMin.y)
  1132             ClipStartTop (line);
  1133           return true;
  1134  
  1135         case 0x96:
  1136           ClipStartLeft (line);
  1137           if (line.start.y > m_clipMax.y)
  1138             return false;
  1139           ClipEndRight (line);
  1140           if (line.end.y < m_clipMin.y)
  1141             return false;
  1142           if (line.start.y < m_clipMin.y)
  1143             ClipStartTop (line);
  1144           if (line.end.y > m_clipMax.y)
  1145             ClipEndBottom (line);
  1146           return true;
  1147  
  1148           // top-right
  1149         case 0xA0:
  1150           ClipStartRight (line);
  1151           if (line.start.y < m_clipMin.y)
  1152             ClipStartTop (line);
  1153           return true;
  1154  
  1155         case 0xA1:
  1156           ClipEndLeft (line);
  1157           if (line.end.y < m_clipMin.y)
  1158             return false;
  1159           ClipStartTop (line);
  1160           if (line.start.x > m_clipMax.x)
  1161             ClipStartRight (line);
  1162           return true;
  1163  
  1164         case 0xA4:
  1165           ClipEndBottom (line);
  1166           if (line.end.x > m_clipMax.x)
  1167             return false;
  1168           ClipStartRight (line);
  1169           if (line.start.y < m_clipMin.y)
  1170             ClipStartTop (line);
  1171           return true;
  1172  
  1173         case 0xA5:
  1174           ClipEndLeft (line);
  1175           if (line.end.y < m_clipMin.y)
  1176             return false;
  1177           ClipStartRight (line);
  1178           if (line.start.y > m_clipMax.y)
  1179             return false;
  1180           if (line.end.y > m_clipMax.y)
  1181             ClipEndBottom (line);
  1182           if (line.start.y < m_clipMin.y)
  1183             ClipStartTop (line);
  1184           return true;
  1185         }
  1186  
  1187       return false;
  1188     }
  1189   };
  1190 }
  1191 
  1192 void
  1193 PyViz::LineClipping (double boundsX1, double boundsY1, double boundsX2, double boundsY2,
  1194                      double &lineX1, double &lineY1, double &lineX2, double &lineY2)
  1195 {
  1196   FastClipping::Vector2 clipMin = {boundsX1, boundsY1}, clipMax = {boundsX2, boundsY2};
  1197   FastClipping::Line line = { { lineX1, lineY1 }, { lineX2, lineY2 }, (lineX2-lineX1), (lineY2-lineY1) };
  1198 
  1199   FastClipping clipper (clipMin, clipMax);
  1200   clipper.ClipLine (line);
  1201   lineX1 = line.start.x;
  1202   lineX2 = line.end.x;
  1203   lineY1 = line.start.y;
  1204   lineY2 = line.end.y;
  1205 }
  1206 
  1207 
  1208 }
  1209