Index: src/helper/animation-interface.cc |
=================================================================== |
--- a/src/helper/animation-interface.cc |
+++ b/src/helper/animation-interface.cc |
@@ -18,8 +18,11 @@ |
// Interface between ns3 and the network animator |
+#include <iomanip> |
#include <stdio.h> |
#include <sstream> |
+#include <string> |
+#include <map> |
#include "ns3/net-anim-config.h" |
@@ -39,6 +42,7 @@ |
#include "ns3/canvas-location.h" |
#include "ns3/packet.h" |
#include "ns3/simulator.h" |
+#include "ns3/mobility-model.h" |
using namespace std; |
@@ -47,12 +51,13 @@ |
namespace ns3 { |
AnimationInterface::AnimationInterface () |
- : m_fHandle (STDOUT_FILENO), m_model (0) |
+ : m_fHandle (STDOUT_FILENO), m_xml(false), m_model (0) |
{ |
} |
AnimationInterface::~AnimationInterface () |
{ |
+ StopAnimation(); |
} |
bool AnimationInterface::SetOutputFile (const std::string& fn) |
@@ -66,6 +71,11 @@ |
return true; |
} |
+void AnimationInterface::SetXMLOutput() |
+{ |
+ m_xml = true; |
+} |
+ |
bool AnimationInterface::SetServerPort (uint16_t port) |
{ |
#if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_NETINET_IN_H) |
@@ -95,20 +105,94 @@ |
void AnimationInterface::StartAnimation () |
{ |
+ // Find the min/max x/y for the xml topology element |
+ double minX = 0; |
+ double minY = 0; |
+ double maxX = 0; |
+ double maxY = 0; |
+ bool first = true; |
+ for (NodeList::Iterator i = NodeList::Begin (); i != NodeList::End (); ++i) |
+ { |
+ Ptr<Node> n = *i; |
+ Ptr<CanvasLocation> loc = n->GetObject<CanvasLocation> (); |
+ Ptr<MobilityModel> mobility = 0; |
+ Vector v; |
+ if (loc) |
+ { |
+ v = loc->GetLocation(); |
+ } |
+ else |
+ { // No canvas location, try position from mobility model |
+ mobility = n->GetObject<MobilityModel>(); |
+ if (mobility) v = mobility->GetPosition(); |
+ } |
+ if (!loc && !mobility) continue; |
+ if (first) |
+ { |
+ minX = v.x; |
+ minY = v.y; |
+ maxX = v.x; |
+ maxY = v.y; |
+ first = false; |
+ } |
+ else |
+ { |
+ minX = min(minX, v.x); |
+ minY = min(minY, v.y); |
+ maxX = max(maxX, v.x); |
+ maxY = max(maxY, v.y); |
+ } |
+ } |
+ |
+ if (m_xml) |
+ { // output the xml headers |
+ // Compute width/height, and add a small margin |
+ double w = maxX - minX; |
+ double h = maxY - minY; |
+ minX -= w * 0.05; |
+ minY -= h * 0.05; |
+ maxX = minX + w * 1.10; |
+ maxY = minY + h * 1.10; |
+ ostringstream oss; |
+ oss << "<anim lp = \"0\">" << endl << "<topology"; |
+ oss << " minX = \"" << minX << "\" minY = \"" << minY |
+ << "\" maxX = \"" << maxX << "\" maxY = \"" << maxY |
+ << "\">" << endl; |
+ WriteN (m_fHandle, oss.str ()); |
+ } |
+ |
// Dump the topology |
for (NodeList::Iterator i = NodeList::Begin (); i != NodeList::End (); ++i) |
{ |
Ptr<Node> n = *i; |
Ptr<CanvasLocation> loc = n->GetObject<CanvasLocation> (); |
+ Ptr<MobilityModel> mobility = 0; |
+ |
+ Vector v; |
if (loc) |
{ |
- // Location exists, dump it |
- Vector v = loc->GetLocation (); |
- ostringstream oss; |
+ v = loc->GetLocation(); |
+ } |
+ else |
+ { // No canvas location, try position from mobility model |
+ mobility = n->GetObject<MobilityModel>(); |
+ if (mobility) v = mobility->GetPosition(); |
+ } |
+ if (!loc && !mobility) continue; // Can't find a position |
+ // Location exists, dump it |
+ ostringstream oss; |
+ if (m_xml) |
+ { |
+ oss << "<node lp = \"0\" id = \"" << n->GetId () |
+ << "\" locX = \"" << v.x |
+ << "\" locY = \"" << v.y << "\"/>" << endl; |
+ } |
+ else |
+ { |
oss << "0.0 N " << n->GetId () |
- << " " << v.x << " " << v.y << endl; |
- WriteN (m_fHandle, oss.str ().c_str (), oss.str ().length ()); |
+ << " " << v.x << " " << v.y << endl; |
} |
+ WriteN (m_fHandle, oss.str ()); |
} |
// Now dump the p2p links |
for (NodeList::Iterator i = NodeList::Begin (); i != NodeList::End (); ++i) |
@@ -136,29 +220,51 @@ |
if (n1Id < n2Id) |
{ // ouptut the p2p link |
ostringstream oss; |
- oss << "0.0 L " << n1Id << " " << n2Id << endl; |
- WriteN (m_fHandle, oss.str ().c_str (), |
- oss.str ().length ()); |
+ if (m_xml) |
+ { |
+ oss << "<link fromLP = \"0\" fromId = \"" << n1Id |
+ << "\" toLp = \"0\" toId = \"" << n2Id |
+ << "\"/>" << endl; |
+ } |
+ else |
+ { |
+ oss << "0.0 L " << n1Id << " " << n2Id << endl; |
+ } |
+ WriteN (m_fHandle, oss.str ()); |
} |
} |
} |
- else |
- { |
- NS_FATAL_ERROR ("Net animation currently only supports point-to-point links."); |
- } |
} |
} |
- |
+ if (m_xml) |
+ { |
+ string endTopo("</topology>\n"); |
+ WriteN(m_fHandle, endTopo); |
+ } |
// Connect the callback for packet tx events |
Config::Connect ("/ChannelList/*/TxRxPointToPoint", |
MakeCallback (&AnimationInterface::DevTxTrace, this)); |
+ Config::Connect ("NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/PhyTxBegin", |
+ MakeCallback (&AnimationInterface::PhyTxBeginTrace, this)); |
+ Config::Connect ("NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/PhyRxBegin", |
+ MakeCallback (&AnimationInterface::PhyRxBeginTrace, this)); |
+ Config::Connect ("NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/PhyRxEnd", |
+ MakeCallback (&AnimationInterface::PhyRxEndTrace, this)); |
+ Config::Connect ("NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/PhyRxDrop", |
+ MakeCallback (&AnimationInterface::PhyRxDropTrace, this)); |
} |
void AnimationInterface::StopAnimation () |
{ |
if (m_fHandle > 0) |
{ |
+ if (m_xml) |
+ { // Terminate the anim element |
+ string endAnim("</anim>\n"); |
+ WriteN (m_fHandle, endAnim); |
+ } |
close (m_fHandle); |
+ m_fHandle = 0; |
} |
} |
@@ -183,20 +289,209 @@ |
} |
return written; |
} |
- |
+ |
+ int AnimationInterface::WriteN(int h, const string& st) |
+ { |
+ return WriteN(h, st.c_str(), st.length()); |
+ } |
+ |
+ |
+ //Trace callbacks |
void AnimationInterface::DevTxTrace (std::string context, Ptr<const Packet> p, |
Ptr<NetDevice> tx, Ptr<NetDevice> rx, |
Time txTime, Time rxTime) |
{ |
Time now = Simulator::Now (); |
ostringstream oss; |
- oss << now.GetSeconds() << " P " |
- << tx->GetNode ()->GetId () << " " |
- << rx->GetNode ()->GetId () << " " |
- << (now + txTime).GetSeconds () << " " // last bit tx time |
- << (now + rxTime - txTime).GetSeconds() << " " // first bit rx time |
- << (now + rxTime).GetSeconds () << endl; // last bit rx time |
- WriteN (m_fHandle, oss.str ().c_str (), oss.str ().length ()); |
+ double fbTx = now.GetSeconds(); |
+ double lbTx = (now + txTime).GetSeconds(); |
+ double fbRx = (now + rxTime - txTime).GetSeconds(); |
+ double lbRx = (now + rxTime).GetSeconds(); |
+ |
+ if (m_xml) |
+ { |
+ oss << setprecision(10); |
+ oss << "<packet fromLp = \"0\" fromId = \"" << tx->GetNode()->GetId() |
+ << "\" fbTx = \"" << fbTx |
+ << "\" lbTx = \"" << lbTx |
+ << "\">" << endl; |
+ oss << "<rx toLp = \"0\" toId = \"" << rx->GetNode()->GetId() |
+ << "\" fbRx = \"" << fbRx |
+ << "\" lbRx = \"" << lbRx |
+ << "\"/>" << endl; |
+ oss << "</packet>" << endl; |
+ } |
+ else |
+ { |
+ oss << setprecision(10); |
+ oss << now.GetSeconds() << " P " |
+ << tx->GetNode ()->GetId () << " " |
+ << rx->GetNode ()->GetId () << " " |
+ << (now + txTime).GetSeconds () << " " // last bit tx time |
+ << (now + rxTime - txTime).GetSeconds() << " " // first bit rx time |
+ << (now + rxTime).GetSeconds () << endl; // last bit rx time |
+ } |
+ WriteN (m_fHandle, oss.str ()); |
+} |
+ |
+// We use a PacketInfo structure for wireless traces to keep up |
+// when each receiver has received packets. Also a PacketRxInfo |
+// to keep up with each receiver and the time it received the packet |
+class RxInfo |
+{ |
+public: |
+ RxInfo(Ptr<const NetDevice> nd, const Time& fbRx) |
+ : m_nd(nd), m_fbRx(fbRx.GetSeconds()), m_lbRx(0) {} |
+ |
+public: |
+ Ptr<const NetDevice> m_nd; // The receiving net device |
+ double m_fbRx; // First bit rx time |
+ double m_lbRx; // Last bit rx time |
+}; |
+ |
+class PacketInfo |
+{ |
+public: |
+ PacketInfo(); |
+ PacketInfo(Ptr<const NetDevice> nd, uint32_t nRx, |
+ const Time& fbTx, const Time& lbTx); |
+ //void AddTxEnd(const Time& lbTx); // Not needed |
+ void AddRxBegin(Ptr<const NetDevice> nd, const Time& fbRx); |
+ bool AddRxEnd(Ptr<const NetDevice> nd, const Time& fbRx); |
+ void AddRxDrop(); |
+public: |
+ Ptr<const NetDevice> m_nd; |
+ uint32_t m_nRx; // Number of receivers expected |
+ uint32_t m_nDrop; // Number of drops |
+ uint32_t m_nRxEnd; // Number of rxEnd callbacks |
+ double m_fbTx; // Time of first bit tx |
+ double m_lbTx; // Time of first bit tx |
+ vector<RxInfo> m_rx; |
+}; |
+ |
+PacketInfo::PacketInfo() |
+ : m_nd(0), m_nRx(0), m_nDrop(0), m_nRxEnd(0), m_fbTx(0), m_lbTx(0) |
+{ |
+} |
+ |
+PacketInfo::PacketInfo(Ptr<const NetDevice> nd, uint32_t nRx, |
+ const Time& fbTx, const Time& lbTx) |
+ : m_nd(nd), m_nRx(nRx), m_nDrop(0), m_nRxEnd(0), |
+ m_fbTx(fbTx.GetSeconds()), m_lbTx(lbTx.GetSeconds()) |
+{ |
+} |
+ |
+void PacketInfo::AddRxBegin(Ptr<const NetDevice> nd, const Time& fbRx) |
+{ |
+ m_rx.push_back(RxInfo(nd, fbRx)); |
+} |
+ |
+bool PacketInfo::AddRxEnd(Ptr<const NetDevice> nd, const Time& lbRx) |
+{ |
+ // Find the RxInfo |
+ for (uint32_t i = 0; i < m_rx.size(); ++i) |
+ { |
+ if (m_rx[i].m_nd == nd) |
+ { // Found it |
+ m_rx[i].m_lbRx = lbRx.GetSeconds(); |
+ m_nRxEnd++; |
+ if ((m_nRxEnd + m_nDrop) == m_nRx) return true; // Got them all |
+ return false; // Still more rxEnd expected |
+ } |
+ } |
+ // This should not happen, but if so we just bump the drop count |
+ m_nDrop++; |
+ if ((m_nRxEnd + m_nDrop) == m_nRx) return true; // Got them all |
+ return false; // Still more rxEnd expected |
+} |
+ |
+void PacketInfo::AddRxDrop() |
+{ |
+ m_nDrop++; |
+} |
+static map<uint32_t, PacketInfo> pendingWirelessPackets; |
+ |
+void AnimationInterface::PhyTxBeginTrace (std::string context, |
+ Ptr<const Packet> p, |
+ Ptr<const NetDevice> nd, |
+ const Time& txTime, |
+ uint32_t nReceivers) |
+{ |
+ // Add a new pending wireless |
+ pendingWirelessPackets[p->GetUid()] = |
+ PacketInfo(nd, nReceivers, |
+ Simulator::Now(), |
+ Simulator::Now() + txTime); |
+} |
+ |
+void AnimationInterface::PhyRxBeginTrace (std::string context, |
+ Ptr<const Packet> p, |
+ Ptr<const NetDevice> nd) |
+{ |
+ pendingWirelessPackets[p->GetUid()].AddRxBegin(nd, Simulator::Now()); |
+} |
+ |
+void AnimationInterface::PhyRxEndTrace (std::string context, |
+ Ptr<const Packet> p, |
+ Ptr<const NetDevice> nd) |
+{ |
+ uint32_t uid = p->GetUid(); |
+ PacketInfo& pkt = pendingWirelessPackets[uid]; |
+ if (pkt.AddRxEnd(nd, Simulator::Now())) |
+ { |
+ OutputWirelessPacket(uid, pkt); |
+ pendingWirelessPackets.erase(pendingWirelessPackets.find(uid));; |
+ } |
+} |
+ |
+void AnimationInterface::PhyRxDropTrace (std::string context, |
+ Ptr<const Packet> p, |
+ Ptr<const NetDevice> nd) |
+{ |
+ pendingWirelessPackets[p->GetUid()].AddRxDrop(); |
+} |
+ |
+// Helper to output a wireless packet. |
+// For now, only the XML interface is supported |
+void AnimationInterface::OutputWirelessPacket(uint32_t uid, PacketInfo& pktInfo) |
+{ |
+ if (!m_xml) return; |
+ ostringstream oss; |
+ uint32_t nodeId = pktInfo.m_nd->GetNode ()->GetId (); |
+ double lbTx = pktInfo.m_lbTx; |
+#ifdef REMOVE_LATER |
+ // This is a hack until the notify tx end is working |
+ if (lbTx == 0) |
+ { |
+ if (pktInfo.m_rx.empty()) |
+ { |
+ lbTx = pktInfo.m_fbTx + 100E-6; // 100 microsec |
+ } |
+ else |
+ { |
+ lbTx = pktInfo.m_fbTx + |
+ pktInfo.m_rx[0].m_lbRx - pktInfo.m_rx[0].m_fbRx; |
+ } |
+ } |
+#endif |
+ // Need to figure out about range |
+ oss << setprecision(10); |
+ oss << "<wpacket fromLp = \"0\" fromId = \"" << nodeId |
+ << "\" fbTx = \"" << pktInfo.m_fbTx |
+ << "\" lbTx = \"" << lbTx |
+ << "\" range = \"500\">" << endl; |
+ // Now add each rx |
+ for (uint32_t i = 0; i < pktInfo.m_rx.size(); ++i) |
+ { |
+ RxInfo& rx = pktInfo.m_rx[i]; |
+ uint32_t rxId = rx.m_nd->GetNode ()->GetId (); |
+ oss << "<rx toLp = \"0\" toId = \"" << rxId |
+ << "\" fbRx = \"" << rx.m_fbRx |
+ << "\" lbRx = \"" << rx.m_lbRx |
+ << "\"/>" << endl; |
+ } |
+ oss << "</wpacket>" << endl; |
+ WriteN (m_fHandle, oss.str ()); |
} |
} // namespace ns3 |