--- a/doc/doxygen.conf Thu Dec 02 07:20:34 2010 -0800
+++ b/doc/doxygen.conf Mon Dec 06 17:26:06 2010 +0000
@@ -607,7 +607,9 @@
src/routing/olsr/olsr-repositories.h \
src/simulator/high-precision.h \
src/simulator/high-precision-128.h \
- src/simulator/high-precision-double.h
+ src/simulator/high-precision-double.h \
+ src/tools/visualizer/model/visual-simulator-impl.h \
+ src/tools/visualizer/model/pyviz.h
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
# directories that are symbolic links (a Unix filesystem feature) are excluded
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/doc/readme.txt Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,9 @@
+NS-3 PyViz is a live simulation visualizer, meaning that it uses no
+trace files. It can be most useful for debugging purposes, i.e. to
+figure out if mobility models are what you expect, where packets are
+being dropped, etc. There's also a builtin interactive python console
+that can be used to debug the state of the running objects. Although
+it is mostly written in Python, it works both with Python and pure C++
+simulations.
+
+For more information, see http://www.nsnam.org/wiki/index.php/PyViz
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/examples/readme.txt Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,3 @@
+For activating the visualizer, with any example, just pass the option
+--SimulatorImplementationType=ns3::VisualSimulatorImpl to it, assuming
+the script uses ns-3's command line parser (class CommandLine).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/model/pyviz.cc Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,1406 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2008 INESC Porto
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Gustavo Carneiro <gjc@inescporto.pt>
+ */
+
+#include <stdlib.h>
+#include "pyviz.h"
+#include "ns3/simulator.h"
+#include "ns3/config.h"
+#include "ns3/node-list.h"
+#include "ns3/wifi-net-device.h"
+#include "ns3/ppp-header.h"
+#include "ns3/wifi-mac-header.h"
+#include "ns3/ethernet-header.h"
+#include "ns3/log.h"
+#include "ns3/abort.h"
+
+#include "visual-simulator-impl.h"
+
+#include <sstream>
+
+NS_LOG_COMPONENT_DEFINE ("PyViz");
+#define NUM_LAST_PACKETS 10
+
+static
+std::vector<std::string>
+PathSplit (std::string str)
+{
+ std::vector<std::string> results;
+ size_t cutAt;
+ while ((cutAt = str.find_first_of('/')) != str.npos)
+ {
+ if(cutAt > 0)
+ {
+ results.push_back(str.substr(0,cutAt));
+ }
+ str = str.substr(cutAt+1);
+ }
+ if (str.length() > 0)
+ {
+ results.push_back(str);
+ }
+ return results;
+}
+
+
+namespace ns3 {
+
+static PyViz* g_visualizer = NULL;
+
+
+
+struct PyVizPacketTag : public Tag
+{
+ static TypeId GetTypeId (void);
+ virtual TypeId GetInstanceTypeId (void) const;
+ virtual uint32_t GetSerializedSize (void) const;
+ virtual void Serialize (TagBuffer buf) const;
+ virtual void Deserialize (TagBuffer buf);
+ virtual void Print (std::ostream &os) const;
+ PyVizPacketTag ();
+
+ uint32_t m_packetId;
+};
+
+
+TypeId
+PyVizPacketTag::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::PyVizPacketTag")
+ .SetParent<Tag> ()
+ .AddConstructor<PyVizPacketTag> ()
+ ;
+ return tid;
+}
+TypeId
+PyVizPacketTag::GetInstanceTypeId (void) const
+{
+ return GetTypeId ();
+}
+uint32_t
+PyVizPacketTag::GetSerializedSize (void) const
+{
+ return 4;
+}
+void
+PyVizPacketTag::Serialize (TagBuffer buf) const
+{
+ buf.WriteU32 (m_packetId);
+}
+void
+PyVizPacketTag::Deserialize (TagBuffer buf)
+{
+ m_packetId = buf.ReadU32 ();
+}
+void
+PyVizPacketTag::Print (std::ostream &os) const
+{
+ os << "PacketId=" << m_packetId;
+}
+PyVizPacketTag::PyVizPacketTag ()
+ : Tag ()
+{}
+
+
+
+PyViz::PyViz ()
+{
+ NS_LOG_FUNCTION_NOARGS ();
+ NS_ASSERT (g_visualizer == NULL);
+ g_visualizer = this;
+
+ Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/MacTx",
+ MakeCallback (&PyViz::TraceNetDevTxWifi, this));
+
+ Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/MacRx",
+ MakeCallback (&PyViz::TraceNetDevRxWifi, this));
+
+ Config::Connect ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacTx",
+ MakeCallback (&PyViz::TraceNetDevTxCsma, this));
+
+ Config::Connect ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacRx",
+ MakeCallback (&PyViz::TraceNetDevRxCsma, this));
+
+ Config::Connect ("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacPromiscRx",
+ MakeCallback (&PyViz::TraceNetDevPromiscRxCsma, this));
+
+ Config::Connect ("/NodeList/*/DeviceList/*/TxQueue/Drop",
+ MakeCallback (&PyViz::TraceDevQueueDrop, this));
+
+ Config::Connect ("/NodeList/*/$ns3::Ipv4L3Protocol/Drop",
+ MakeCallback (&PyViz::TraceIpv4Drop, this));
+
+ Config::Connect ("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/MacTx",
+ MakeCallback (&PyViz::TraceNetDevTxPointToPoint, this));
+
+ Config::Connect ("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/MacRx",
+ MakeCallback (&PyViz::TraceNetDevRxPointToPoint, this));
+
+ Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WimaxNetDevice/Tx",
+ MakeCallback (&PyViz::TraceNetDevTxWimax, this));
+
+ Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WimaxNetDevice/Rx",
+ MakeCallback (&PyViz::TraceNetDevRxWimax, this));
+
+}
+
+void
+PyViz::RegisterCsmaLikeDevice (std::string const &deviceTypeName)
+{
+ TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
+
+ std::ostringstream sstream;
+ sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/MacTx";
+ Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxCsma, this));
+
+ sstream.str ("");
+ sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
+ Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxCsma, this));
+
+ sstream.str ("");
+ sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/PromiscRx";
+ Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevPromiscRxCsma, this));
+}
+
+void
+PyViz::RegisterWifiLikeDevice (std::string const &deviceTypeName)
+{
+ TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
+
+ std::ostringstream sstream;
+ sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Tx";
+ Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxWifi, this));
+
+ sstream.str ("");
+ sstream <<"/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
+ Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxWifi, this));
+}
+
+void
+PyViz::RegisterPointToPointLikeDevice (std::string const &deviceTypeName)
+{
+ TypeId::LookupByName (deviceTypeName); // this will assert if the type name is invalid
+
+ std::ostringstream sstream;
+ sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/TxQueue/Dequeue";
+ Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevTxPointToPoint, this));
+
+ sstream.str ("");
+ sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
+ Config::Connect (sstream.str (), MakeCallback (&PyViz::TraceNetDevRxPointToPoint, this));
+}
+
+void
+PyViz::SetPacketCaptureOptions (uint32_t nodeId, PacketCaptureOptions options)
+{
+ NS_LOG_DEBUG (" SetPacketCaptureOptions " << nodeId
+ << " PacketCaptureOptions (headers size = " << options.headers.size ()
+ << " mode = " << options.mode << " numLastPackets = " << options.numLastPackets
+ << ")");
+ m_packetCaptureOptions[nodeId] = options;
+}
+
+void
+PyViz::RegisterDropTracePath (std::string const &tracePath)
+{
+ Config::Connect (tracePath, MakeCallback (&PyViz::TraceDevQueueDrop, this));
+}
+
+PyViz::~PyViz ()
+{
+ NS_LOG_FUNCTION_NOARGS ();
+
+ NS_ASSERT (g_visualizer == this);
+ g_visualizer = NULL;
+}
+
+void PyViz::DoPause (std::string const &message)
+{
+ m_pauseMessages.push_back (message);
+ m_stop = true;
+ NS_LOG_LOGIC (Simulator::Now ().GetSeconds () << ": Have "
+ << g_visualizer->m_pauseMessages.size () << " pause messages");
+}
+
+void PyViz::Pause (std::string const &message)
+{
+ NS_ASSERT (g_visualizer);
+ g_visualizer->DoPause (message);
+}
+
+std::vector<std::string>
+PyViz::GetPauseMessages () const
+{
+ NS_LOG_LOGIC (Simulator::Now ().GetSeconds () << ": GetPauseMessages: have "
+ << g_visualizer->m_pauseMessages.size () << " pause messages");
+ return m_pauseMessages;
+}
+
+
+void
+PyViz::CallbackStopSimulation ()
+{
+ NS_LOG_FUNCTION_NOARGS ();
+ Simulator::Stop (Seconds (0)); // Stop right now
+ m_stop = true;
+}
+
+void
+PyViz::SimulatorRunUntil (Time time)
+{
+ NS_LOG_LOGIC ("SimulatorRunUntil " << time << " (now is " << Simulator::Now () << ")");
+
+ m_pauseMessages.clear ();
+ m_transmissionSamples.clear ();
+ m_packetDrops.clear ();
+
+ Time expirationTime = Simulator::Now () - Seconds (10);
+
+ // Clear very old transmission records
+ for (std::map<TxRecordKey, TxRecordValue>::iterator iter = m_txRecords.begin ();
+ iter != m_txRecords.end ();)
+ {
+ if (iter->second.time < expirationTime)
+ {
+ m_txRecords.erase (iter++);
+ }
+ else
+ {
+ iter++;
+ }
+ }
+
+ // Clear very old packets of interest
+ for (std::map<uint32_t, Time>::iterator iter = m_packetsOfInterest.begin ();
+ iter != m_packetsOfInterest.end ();)
+ {
+ if (iter->second < expirationTime)
+ {
+ m_packetsOfInterest.erase (iter++);
+ }
+ else
+ {
+ iter++;
+ }
+ }
+
+ if (Simulator::Now () >= time)
+ {
+ return;
+ }
+ // Schedule a dummy callback function for the target time, to make
+ // sure we stop at the right time. Otherwise, simulations with few
+ // events just appear to "jump" big chunks of time.
+ NS_LOG_LOGIC ("Schedule dummy callback to be called in " << (time - Simulator::Now ()));
+ m_stop = false;
+ Simulator::Cancel (m_stopCallbackEvent);
+ m_stopCallbackEvent = Simulator::Schedule (time - Simulator::Now (), &PyViz::CallbackStopSimulation, this);
+
+ Ptr<SimulatorImpl> impl = Simulator::GetImplementation ();
+ Ptr<VisualSimulatorImpl> visualImpl = DynamicCast<VisualSimulatorImpl> (impl);
+ if (visualImpl)
+ {
+ visualImpl->RunRealSimulator ();
+ }
+ else
+ {
+ impl->Run ();
+ }
+}
+
+bool PyViz::TransmissionSampleKey::operator < (PyViz::TransmissionSampleKey const &other) const
+{
+ if (this->transmitter < other.transmitter)
+ {
+ return true;
+ }
+ if (this->transmitter != other.transmitter)
+ {
+ return false;
+ }
+ if (this->receiver < other.receiver)
+ {
+ return true;}
+ if (this->receiver != other.receiver)
+ {
+ return false;
+ }
+ if (this->channel < other.channel)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool PyViz::TransmissionSampleKey::operator == (PyViz::TransmissionSampleKey const &other) const
+{
+ bool retval = (transmitter == other.transmitter) &&
+ (receiver == other.receiver) &&
+ (channel == other.channel);
+ return retval;
+}
+
+
+PyViz::NetDeviceStatistics &
+PyViz::FindNetDeviceStatistics (int node, int interface)
+{
+ std::map<uint32_t, std::vector<NetDeviceStatistics> >::iterator nodeStatsIter = m_nodesStatistics.find (node);
+ std::vector<NetDeviceStatistics> *stats;
+ if (nodeStatsIter == m_nodesStatistics.end ())
+ {
+ stats = &m_nodesStatistics[node];
+ stats->resize (NodeList::GetNode (node)->GetNDevices ());
+ }
+ else
+ {
+ stats = &(nodeStatsIter->second);
+ }
+ NetDeviceStatistics &devStats = (*stats)[interface];
+ return devStats;
+}
+
+bool PyViz::GetPacketCaptureOptions (uint32_t nodeId, const PacketCaptureOptions **outOptions) const
+{
+ std::map<uint32_t, PacketCaptureOptions>::const_iterator iter = m_packetCaptureOptions.find (nodeId);
+ if (iter == m_packetCaptureOptions.end ())
+ {
+ return false;
+ }
+ else
+ {
+ *outOptions = &iter->second;
+ return true;
+ }
+}
+
+bool PyViz::FilterPacket (Ptr<const Packet> packet, const PacketCaptureOptions &options)
+{
+ switch (options.mode)
+ {
+ case PACKET_CAPTURE_DISABLED:
+ return false;
+
+ case PACKET_CAPTURE_FILTER_HEADERS_OR:
+ {
+ PacketMetadata::ItemIterator metadataIterator = packet->BeginItem ();
+ while (metadataIterator.HasNext ())
+ {
+ PacketMetadata::Item item = metadataIterator.Next ();
+ if (options.headers.find (item.tid) != options.headers.end ())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ case PACKET_CAPTURE_FILTER_HEADERS_AND:
+ {
+ std::set<TypeId> missingHeaders (options.headers);
+ PacketMetadata::ItemIterator metadataIterator = packet->BeginItem ();
+ while (metadataIterator.HasNext ())
+ {
+ PacketMetadata::Item item = metadataIterator.Next ();
+ std::set<TypeId>::iterator missingIter = missingHeaders.find (item.tid);
+ if (missingIter != missingHeaders.end ())
+ {
+ missingHeaders.erase (missingIter);
+ }
+ }
+ if (missingHeaders.size () == 0)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ default:
+ NS_FATAL_ERROR ("should not be reached");
+ return false;
+ }
+}
+
+void
+PyViz::TraceDevQueueDrop (std::string context, Ptr<const Packet> packet)
+{
+ NS_LOG_FUNCTION (context << packet->GetUid ());
+ std::vector<std::string> splitPath = PathSplit (context);
+ int nodeIndex = atoi (splitPath[1].c_str ());
+ Ptr<Node> node = NodeList::GetNode (nodeIndex);
+
+ if (m_nodesOfInterest.find (nodeIndex) == m_nodesOfInterest.end ())
+ {
+ // if the transmitting node is not "of interest", we still
+ // record the transmission if it is a packet of interest.
+ if (m_packetsOfInterest.find (packet->GetUid ()) == m_packetsOfInterest.end ())
+ {
+ NS_LOG_DEBUG ("Packet " << packet->GetUid () << " is not of interest");
+ return;
+ }
+ }
+
+ // ---- "last packets"
+ const PacketCaptureOptions *captureOptions;
+ if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
+ {
+ LastPacketsSample &last = m_lastPackets[nodeIndex];
+ PacketSample lastPacket;
+ lastPacket.time = Simulator::Now ();
+ lastPacket.packet = packet->Copy ();
+ lastPacket.device = NULL;
+ last.lastDroppedPackets.push_back (lastPacket);
+ while (last.lastDroppedPackets.size () > captureOptions->numLastPackets)
+ {
+ last.lastDroppedPackets.erase (last.lastDroppedPackets.begin ());
+ }
+ }
+
+ std::map<Ptr<Node>, uint32_t>::iterator iter = m_packetDrops.find (node);
+ if (iter == m_packetDrops.end ())
+ {
+ m_packetDrops[node] = packet->GetSize ();
+ }
+ else
+ {
+ iter->second += packet->GetSize ();
+ }
+}
+
+void
+PyViz::TraceIpv4Drop (std::string context, ns3::Ipv4Header const &hdr, Ptr<const Packet> packet,
+ ns3::Ipv4L3Protocol::DropReason reason, Ptr<Ipv4> dummy_ipv4, uint32_t interface)
+{
+ Ptr<Packet> packetCopy = packet->Copy ();
+ packetCopy->AddHeader (hdr);
+ TraceDevQueueDrop (context, packetCopy);
+}
+
+
+ // --------- TX device tracing -------------------
+
+void
+PyViz::TraceNetDevTxCommon (std::string const &context, Ptr<const Packet> packet,
+ Mac48Address const &destinationAddress)
+{
+ NS_LOG_FUNCTION (context << packet->GetUid () << *packet);
+
+ std::vector<std::string> splitPath = PathSplit (context);
+ int nodeIndex = atoi (splitPath[1].c_str ());
+ int devIndex = atoi (splitPath[3].c_str ());
+ Ptr<Node> node = NodeList::GetNode (nodeIndex);
+ Ptr<NetDevice> device = node->GetDevice (devIndex);
+
+ // ---- statistics
+ NetDeviceStatistics &stats = FindNetDeviceStatistics (nodeIndex, devIndex);
+ ++stats.transmittedPackets;
+ stats.transmittedBytes += packet->GetSize ();
+
+ // ---- "last packets"
+ const PacketCaptureOptions *captureOptions;
+ if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
+ {
+ LastPacketsSample &last = m_lastPackets[nodeIndex];
+ TxPacketSample lastPacket;
+ lastPacket.time = Simulator::Now ();
+ lastPacket.packet = packet->Copy ();
+ lastPacket.device = device;
+ lastPacket.to = destinationAddress;
+ last.lastTransmittedPackets.push_back (lastPacket);
+ while (last.lastTransmittedPackets.size () > captureOptions->numLastPackets)
+ {
+ last.lastTransmittedPackets.erase (last.lastTransmittedPackets.begin ());
+ }
+ }
+
+ // ---- transmissions records
+
+ if (m_nodesOfInterest.find (nodeIndex) == m_nodesOfInterest.end ())
+ {
+ // if the transmitting node is not "of interest", we still
+ // record the transmission if it is a packet of interest.
+ if (m_packetsOfInterest.find (packet->GetUid ()) == m_packetsOfInterest.end ())
+ {
+ NS_LOG_DEBUG ("Packet " << packet->GetUid () << " is not of interest");
+ return;
+ }
+ }
+ else
+ {
+ // We will follow this packet throughout the network.
+ m_packetsOfInterest[packet->GetUid ()] = Simulator::Now ();
+ }
+
+ TxRecordValue record = { Simulator::Now (), node, false };
+ if (destinationAddress == device->GetBroadcast ())
+ {
+ record.isBroadcast = true;
+ }
+
+ m_txRecords[TxRecordKey (device->GetChannel (), packet->GetUid ())] = record;
+
+ PyVizPacketTag tag;
+ //packet->RemovePacketTag (tag);
+ tag.m_packetId = packet->GetUid ();
+ packet->AddByteTag (tag);
+}
+
+void
+PyViz::TraceNetDevTxWifi (std::string context, Ptr<const Packet> packet)
+{
+ NS_LOG_FUNCTION (context << packet->GetUid () << *packet);
+
+ /*
+ * To DS From DS Address 1 Address 2 Address 3 Address 4
+ *----------------------------------------------------------------------
+ * 0 0 Destination Source BSSID N/A
+ * 0 1 Destination BSSID Source N/A
+ * 1 0 BSSID Source Destination N/A
+ * 1 1 Receiver Transmitter Destination Source
+ */
+ WifiMacHeader hdr;
+ NS_ABORT_IF (packet->PeekHeader (hdr) == 0);
+ Mac48Address destinationAddress;
+ if (hdr.IsToDs () && !hdr.IsFromDs ())
+ {
+ destinationAddress = hdr.GetAddr3 ();
+ }
+ else if (!hdr.IsToDs () && hdr.IsFromDs ())
+ {
+ destinationAddress = hdr.GetAddr1 ();
+ }
+ else if (!hdr.IsToDs () && !hdr.IsFromDs ())
+ {
+ destinationAddress = hdr.GetAddr1 ();
+ }
+ else
+ {
+ destinationAddress = hdr.GetAddr3 ();
+ }
+ TraceNetDevTxCommon (context, packet, destinationAddress);
+}
+
+
+void
+PyViz::TraceNetDevTxCsma (std::string context, Ptr<const Packet> packet)
+{
+ EthernetHeader ethernetHeader;
+ NS_ABORT_IF (packet->PeekHeader (ethernetHeader) == 0);
+ TraceNetDevTxCommon (context, packet, ethernetHeader.GetDestination ());
+}
+
+void
+PyViz::TraceNetDevTxPointToPoint (std::string context, Ptr<const Packet> packet)
+{
+ TraceNetDevTxCommon (context, packet, Mac48Address ());
+}
+
+
+
+
+ // --------- RX device tracing -------------------
+
+void
+PyViz::TraceNetDevRxCommon (std::string const &context, Ptr<const Packet> packet, Mac48Address const &from)
+{
+ uint32_t uid;
+ PyVizPacketTag tag;
+ if (packet->FindFirstMatchingByteTag (tag))
+ {
+ uid = tag.m_packetId;
+ }
+ else
+ {
+ //NS_ASSERT (0);
+ NS_LOG_WARN ("Packet has no byte tag; wimax link?");
+ uid = packet->GetUid ();
+ }
+
+ NS_LOG_FUNCTION (context << uid);
+ std::vector<std::string> splitPath = PathSplit (context);
+ int nodeIndex = atoi (splitPath[1].c_str ());
+ int devIndex = atoi (splitPath[3].c_str ());
+
+ // ---- statistics
+ NetDeviceStatistics &stats = FindNetDeviceStatistics (nodeIndex, devIndex);
+ ++stats.receivedPackets;
+ stats.receivedBytes += packet->GetSize ();
+
+ Ptr<Node> node = NodeList::GetNode (nodeIndex);
+ Ptr<NetDevice> device = node->GetDevice (devIndex);
+
+ // ---- "last packets"
+ const PacketCaptureOptions *captureOptions;
+ if (GetPacketCaptureOptions (nodeIndex, &captureOptions) && FilterPacket (packet, *captureOptions))
+ {
+ LastPacketsSample &last = m_lastPackets[nodeIndex];
+ RxPacketSample lastPacket;
+ lastPacket.time = Simulator::Now ();
+ lastPacket.packet = packet->Copy ();
+ lastPacket.device = device;
+ lastPacket.from = from;
+ last.lastReceivedPackets.push_back (lastPacket);
+ while (last.lastReceivedPackets.size () > captureOptions->numLastPackets)
+ {
+ last.lastReceivedPackets.erase (last.lastReceivedPackets.begin ());
+ }
+ }
+
+ // ---- transmissions
+ if (m_packetsOfInterest.find (uid) == m_packetsOfInterest.end ())
+ {
+ NS_LOG_DEBUG ("RX Packet " << uid << " is not of interest");
+ return;
+ }
+
+ Ptr<Channel> channel = device->GetChannel ();
+
+ std::map<TxRecordKey, TxRecordValue>::iterator recordIter =
+ m_txRecords.find (TxRecordKey (channel, uid));
+
+ if (recordIter == m_txRecords.end ())
+ {
+ NS_LOG_DEBUG ("RX Packet " << uid << " was not transmitted?!");
+ return;
+ }
+
+ TxRecordValue &record = recordIter->second;
+
+ if (record.srcNode == node)
+ {
+ NS_LOG_WARN ("Node " << node->GetId () << " receiving back the same packet (UID=" << uid
+ << ") it had previously transmitted, on the same channel!");
+ return;
+ }
+
+ TransmissionSampleKey key = { record.srcNode, node, channel };
+
+#ifdef NS3_LOG_ENABLE
+ NS_LOG_DEBUG("m_transmissionSamples begin:");
+ if (g_log.IsEnabled (ns3::LOG_DEBUG))
+ {
+ for (std::map<TransmissionSampleKey,TransmissionSampleValue>::const_iterator iter
+ = m_transmissionSamples.begin (); iter != m_transmissionSamples.end (); iter++)
+ {
+ NS_LOG_DEBUG(iter->first.transmitter<<"/"<<iter->first.transmitter->GetId () << ", "
+ << iter->first.receiver<<"/"<<iter->first.receiver->GetId ()
+ << ", " << iter->first.channel << " => " << iter->second.bytes << " (@ " << &iter->second << ")");
+ }
+ }
+ NS_LOG_DEBUG("m_transmissionSamples end.");
+#endif
+
+ std::map<TransmissionSampleKey,TransmissionSampleValue>::iterator
+ iter = m_transmissionSamples.find (key);
+
+ if (iter == m_transmissionSamples.end ())
+ {
+ TransmissionSampleValue sample = { packet->GetSize () };
+ NS_LOG_DEBUG ("RX: from " << key.transmitter<<"/"<<key.transmitter->GetId() << " to "
+ << key.receiver<<"/"<<key.receiver->GetId()
+ << " channel " << channel << ": " << packet->GetSize ()
+ << " bytes more. => new sample with " << packet->GetSize () << " bytes.");
+ m_transmissionSamples[key] = sample;
+ }
+ else
+ {
+ TransmissionSampleValue &sample = iter->second;
+ NS_LOG_DEBUG ("RX: from " << key.transmitter<<"/"<<key.transmitter->GetId() << " to "
+ << key.receiver<<"/"<<key.receiver->GetId()
+ << " channel " << channel << ": " << packet->GetSize ()
+ << " bytes more. => sample " << &sample << " with bytes " << sample.bytes);
+
+ sample.bytes += packet->GetSize ();
+ }
+}
+
+void
+PyViz::TraceNetDevRxWifi (std::string context, Ptr<const Packet> packet)
+{
+ NS_LOG_FUNCTION (context << packet->GetUid ());
+
+
+ /*
+ * To DS From DS Address 1 Address 2 Address 3 Address 4
+ *----------------------------------------------------------------------
+ * 0 0 Destination Source BSSID N/A
+ * 0 1 Destination BSSID Source N/A
+ * 1 0 BSSID Source Destination N/A
+ * 1 1 Receiver Transmitter Destination Source
+ */
+ WifiMacHeader hdr;
+ NS_ABORT_IF (packet->PeekHeader (hdr) == 0);
+ Mac48Address sourceAddress;
+ if (hdr.IsToDs () && !hdr.IsFromDs ())
+ {
+ sourceAddress = hdr.GetAddr2 ();
+ }
+ else if (!hdr.IsToDs () && hdr.IsFromDs ())
+ {
+ sourceAddress = hdr.GetAddr3 ();
+ }
+ else if (!hdr.IsToDs () && !hdr.IsFromDs ())
+ {
+ sourceAddress = hdr.GetAddr2 ();
+ }
+ else
+ {
+ sourceAddress = hdr.GetAddr4 ();
+ }
+
+ TraceNetDevRxCommon (context, packet, sourceAddress);
+}
+
+
+
+void
+PyViz::TraceNetDevRxCsma (std::string context, Ptr<const Packet> packet)
+{
+ EthernetHeader ethernetHeader;
+ NS_ABORT_IF (packet->PeekHeader (ethernetHeader) == 0);
+ TraceNetDevRxCommon (context, packet, ethernetHeader.GetSource ());
+}
+
+void
+PyViz::TraceNetDevRxPointToPoint (std::string context, Ptr<const Packet> packet)
+{
+ TraceNetDevRxCommon (context, packet, Mac48Address ());
+}
+
+void
+PyViz::TraceNetDevPromiscRxCsma (std::string context, Ptr<const Packet> packet)
+{
+ EthernetHeader ethernetHeader;
+ NS_ABORT_IF (packet->PeekHeader (ethernetHeader) == 0);
+
+ NetDevice::PacketType packetType = NetDevice::PACKET_OTHERHOST; // FIXME
+
+ // Other packet types are already being received by
+ // TraceNetDevRxCsma; we don't want to receive them twice.
+ if (packetType == NetDevice::PACKET_OTHERHOST)
+ {
+ TraceNetDevRxCommon (context, packet, ethernetHeader.GetDestination ());
+ }
+}
+
+void
+PyViz::TraceNetDevTxWimax (std::string context, Ptr<const Packet> packet, Mac48Address const &destination)
+{
+ NS_LOG_FUNCTION (context);
+ TraceNetDevTxCommon (context, packet, destination);
+}
+
+void
+PyViz::TraceNetDevRxWimax (std::string context, Ptr<const Packet> packet, Mac48Address const &source)
+{
+ NS_LOG_FUNCTION (context);
+ TraceNetDevRxCommon (context, packet, source);
+}
+
+
+ // ---------------------
+
+PyViz::TransmissionSampleList
+PyViz::GetTransmissionSamples () const
+{
+ NS_LOG_DEBUG ("GetTransmissionSamples BEGIN");
+ TransmissionSampleList list;
+ for (std::map<TransmissionSampleKey, TransmissionSampleValue>::const_iterator
+ iter = m_transmissionSamples.begin ();
+ iter != m_transmissionSamples.end ();
+ iter++)
+ {
+ TransmissionSample sample;
+ sample.transmitter = iter->first.transmitter;
+ sample.receiver = iter->first.receiver;
+ sample.channel = iter->first.channel;
+ sample.bytes = iter->second.bytes;
+ NS_LOG_DEBUG ("from " << sample.transmitter->GetId() << " to " << sample.receiver->GetId()
+ << ": " << sample.bytes << " bytes.");
+ list.push_back (sample);
+ }
+ NS_LOG_DEBUG ("GetTransmissionSamples END");
+ return list;
+}
+
+PyViz::PacketDropSampleList
+PyViz::GetPacketDropSamples () const
+{
+ NS_LOG_DEBUG ("GetPacketDropSamples BEGIN");
+ PacketDropSampleList list;
+ for (std::map<Ptr<Node>, uint32_t>::const_iterator
+ iter = m_packetDrops.begin ();
+ iter != m_packetDrops.end ();
+ iter++)
+ {
+ PacketDropSample sample;
+ sample.transmitter = iter->first;
+ sample.bytes = iter->second;
+ NS_LOG_DEBUG ("in " << sample.transmitter->GetId()
+ << ": " << sample.bytes << " bytes dropped.");
+ list.push_back (sample);
+ }
+ NS_LOG_DEBUG ("GetPacketDropSamples END");
+ return list;
+}
+
+void
+PyViz::SetNodesOfInterest (std::set<uint32_t> nodes)
+{
+ m_nodesOfInterest = nodes;
+}
+
+std::vector<PyViz::NodeStatistics>
+PyViz::GetNodesStatistics () const
+{
+ std::vector<PyViz::NodeStatistics> retval;
+ for (std::map<uint32_t, std::vector<NetDeviceStatistics> >::const_iterator iter = m_nodesStatistics.begin ();
+ iter != m_nodesStatistics.end (); iter++)
+ {
+ NodeStatistics stats = { iter->first, iter->second };
+ retval.push_back (stats);
+ }
+ return retval;
+}
+
+
+PyViz::LastPacketsSample
+PyViz::GetLastPackets (uint32_t nodeId) const
+{
+ NS_LOG_DEBUG ("GetLastPackets: " << nodeId);
+
+ std::map<uint32_t, LastPacketsSample>::const_iterator
+ iter = m_lastPackets.find(nodeId);
+ if (iter != m_lastPackets.end ())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return LastPacketsSample ();
+ }
+}
+
+
+
+
+
+
+namespace
+{
+ // Adapted from http://en.wikipedia.org/w/index.php?title=Line_clipping&oldid=248609574
+ class FastClipping
+ {
+ public:
+ struct Vector2
+ {
+ double x;
+ double y;
+ };
+
+ Vector2 m_clipMin, m_clipMax;
+
+ struct Line
+ {
+ Vector2 start, end;
+ double dx, dy;
+ };
+
+ private:
+
+ void ClipStartTop (Line &line)
+ {
+ line.start.x += line.dx * (m_clipMin.y - line.start.y) / line.dy;
+ line.start.y = m_clipMin.y;
+ }
+
+ void ClipStartBottom (Line &line)
+ {
+ line.start.x += line.dx * (m_clipMax.y - line.start.y) / line.dy;
+ line.start.y = m_clipMax.y;
+ }
+
+ void ClipStartRight (Line &line)
+ {
+ line.start.y += line.dy * (m_clipMax.x - line.start.x) / line.dx;
+ line.start.x = m_clipMax.x;
+ }
+
+ void ClipStartLeft (Line &line)
+ {
+ line.start.y += line.dy * (m_clipMin.x - line.start.x) / line.dx;
+ line.start.x = m_clipMin.x;
+ }
+
+ void ClipEndTop (Line &line)
+ {
+ line.end.x += line.dx * (m_clipMin.y - line.end.y) / line.dy;
+ line.end.y = m_clipMin.y;
+ }
+
+ void ClipEndBottom (Line &line) {
+ line.end.x += line.dx * (m_clipMax.y - line.end.y) / line.dy;
+ line.end.y = m_clipMax.y;
+ }
+
+ void ClipEndRight (Line &line)
+ {
+ line.end.y += line.dy * (m_clipMax.x - line.end.x) / line.dx;
+ line.end.x = m_clipMax.x;
+ }
+
+ void ClipEndLeft (Line &line)
+ {
+ line.end.y += line.dy * (m_clipMin.x - line.end.x) / line.dx;
+ line.end.x = m_clipMin.x;
+ }
+
+ public:
+ FastClipping (Vector2 clipMin, Vector2 clipMax)
+ : m_clipMin (clipMin), m_clipMax (clipMax)
+ {
+ }
+
+
+ bool ClipLine (Line &line)
+ {
+ uint8_t lineCode = 0;
+
+ if (line.end.y < m_clipMin.y)
+ lineCode |= 8;
+ else if (line.end.y > m_clipMax.y)
+ lineCode |= 4;
+
+ if (line.end.x > m_clipMax.x)
+ lineCode |= 2;
+ else if (line.end.x < m_clipMin.x)
+ lineCode |= 1;
+
+ if (line.start.y < m_clipMin.y)
+ lineCode |= 128;
+ else if (line.start.y > m_clipMax.y)
+ lineCode |= 64;
+
+ if (line.start.x > m_clipMax.x)
+ lineCode |= 32;
+ else if (line.start.x < m_clipMin.x)
+ lineCode |= 16;
+
+ // 9 - 8 - A
+ // | | |
+ // 1 - 0 - 2
+ // | | |
+ // 5 - 4 - 6
+ switch (lineCode)
+ {
+ // center
+ case 0x00:
+ return true;
+
+ case 0x01:
+ ClipEndLeft (line);
+ return true;
+
+ case 0x02:
+ ClipEndRight (line);
+ return true;
+
+ case 0x04:
+ ClipEndBottom (line);
+ return true;
+
+ case 0x05:
+ ClipEndLeft (line);
+ if (line.end.y > m_clipMax.y)
+ ClipEndBottom (line);
+ return true;
+
+ case 0x06:
+ ClipEndRight (line);
+ if (line.end.y > m_clipMax.y)
+ ClipEndBottom (line);
+ return true;
+
+ case 0x08:
+ ClipEndTop (line);
+ return true;
+
+ case 0x09:
+ ClipEndLeft (line);
+ if (line.end.y < m_clipMin.y)
+ ClipEndTop (line);
+ return true;
+
+ case 0x0A:
+ ClipEndRight (line);
+ if (line.end.y < m_clipMin.y)
+ ClipEndTop (line);
+ return true;
+
+ // left
+ case 0x10:
+ ClipStartLeft (line);
+ return true;
+
+ case 0x12:
+ ClipStartLeft (line);
+ ClipEndRight (line);
+ return true;
+
+ case 0x14:
+ ClipStartLeft (line);
+ if (line.start.y > m_clipMax.y)
+ return false;
+ ClipEndBottom (line);
+ return true;
+
+ case 0x16:
+ ClipStartLeft (line);
+ if (line.start.y > m_clipMax.y)
+ return false;
+ ClipEndBottom (line);
+ if (line.end.x > m_clipMax.x)
+ ClipEndRight (line);
+ return true;
+
+ case 0x18:
+ ClipStartLeft (line);
+ if (line.start.y < m_clipMin.y)
+ return false;
+ ClipEndTop (line);
+ return true;
+
+ case 0x1A:
+ ClipStartLeft (line);
+ if (line.start.y < m_clipMin.y)
+ return false;
+ ClipEndTop (line);
+ if (line.end.x > m_clipMax.x)
+ ClipEndRight (line);
+ return true;
+
+ // right
+ case 0x20:
+ ClipStartRight (line);
+ return true;
+
+ case 0x21:
+ ClipStartRight (line);
+ ClipEndLeft (line);
+ return true;
+
+ case 0x24:
+ ClipStartRight (line);
+ if (line.start.y > m_clipMax.y)
+ return false;
+ ClipEndBottom (line);
+ return true;
+
+ case 0x25:
+ ClipStartRight (line);
+ if (line.start.y > m_clipMax.y)
+ return false;
+ ClipEndBottom (line);
+ if (line.end.x < m_clipMin.x)
+ ClipEndLeft (line);
+ return true;
+
+ case 0x28:
+ ClipStartRight (line);
+ if (line.start.y < m_clipMin.y)
+ return false;
+ ClipEndTop (line);
+ return true;
+
+ case 0x29:
+ ClipStartRight (line);
+ if (line.start.y < m_clipMin.y)
+ return false;
+ ClipEndTop (line);
+ if (line.end.x < m_clipMin.x)
+ ClipEndLeft (line);
+ return true;
+
+ // bottom
+ case 0x40:
+ ClipStartBottom (line);
+ return true;
+
+ case 0x41:
+ ClipStartBottom (line);
+ if (line.start.x < m_clipMin.x)
+ return false;
+ ClipEndLeft (line);
+ if (line.end.y > m_clipMax.y)
+ ClipEndBottom (line);
+ return true;
+
+ case 0x42:
+ ClipStartBottom (line);
+ if (line.start.x > m_clipMax.x)
+ return false;
+ ClipEndRight (line);
+ return true;
+
+ case 0x48:
+ ClipStartBottom (line);
+ ClipEndTop (line);
+ return true;
+
+ case 0x49:
+ ClipStartBottom (line);
+ if (line.start.x < m_clipMin.x)
+ return false;
+ ClipEndLeft (line);
+ if (line.end.y < m_clipMin.y)
+ ClipEndTop (line);
+ return true;
+
+ case 0x4A:
+ ClipStartBottom (line);
+ if (line.start.x > m_clipMax.x)
+ return false;
+ ClipEndRight (line);
+ if (line.end.y < m_clipMin.y)
+ ClipEndTop (line);
+ return true;
+
+ // bottom-left
+ case 0x50:
+ ClipStartLeft (line);
+ if (line.start.y > m_clipMax.y)
+ ClipStartBottom (line);
+ return true;
+
+ case 0x52:
+ ClipEndRight (line);
+ if (line.end.y > m_clipMax.y)
+ return false;
+ ClipStartBottom (line);
+ if (line.start.x < m_clipMin.x)
+ ClipStartLeft (line);
+ return true;
+
+ case 0x58:
+ ClipEndTop (line);
+ if (line.end.x < m_clipMin.x)
+ return false;
+ ClipStartBottom (line);
+ if (line.start.x < m_clipMin.x)
+ ClipStartLeft (line);
+ return true;
+
+ case 0x5A:
+ ClipStartLeft (line);
+ if (line.start.y < m_clipMin.y)
+ return false;
+ ClipEndRight (line);
+ if (line.end.y > m_clipMax.y)
+ return false;
+ if (line.start.y > m_clipMax.y)
+ ClipStartBottom (line);
+ if (line.end.y < m_clipMin.y)
+ ClipEndTop (line);
+ return true;
+
+ // bottom-right
+ case 0x60:
+ ClipStartRight (line);
+ if (line.start.y > m_clipMax.y)
+ ClipStartBottom (line);
+ return true;
+
+ case 0x61:
+ ClipEndLeft (line);
+ if (line.end.y > m_clipMax.y)
+ return false;
+ ClipStartBottom (line);
+ if (line.start.x > m_clipMax.x)
+ ClipStartRight (line);
+ return true;
+
+ case 0x68:
+ ClipEndTop (line);
+ if (line.end.x > m_clipMax.x)
+ return false;
+ ClipStartRight (line);
+ if (line.start.y > m_clipMax.y)
+ ClipStartBottom (line);
+ return true;
+
+ case 0x69:
+ ClipEndLeft (line);
+ if (line.end.y > m_clipMax.y)
+ return false;
+ ClipStartRight (line);
+ if (line.start.y < m_clipMin.y)
+ return false;
+ if (line.end.y < m_clipMin.y)
+ ClipEndTop (line);
+ if (line.start.y > m_clipMax.y)
+ ClipStartBottom (line);
+ return true;
+
+ // top
+ case 0x80:
+ ClipStartTop (line);
+ return true;
+
+ case 0x81:
+ ClipStartTop (line);
+ if (line.start.x < m_clipMin.x)
+ return false;
+ ClipEndLeft (line);
+ return true;
+
+ case 0x82:
+ ClipStartTop (line);
+ if (line.start.x > m_clipMax.x)
+ return false;
+ ClipEndRight (line);
+ return true;
+
+ case 0x84:
+ ClipStartTop (line);
+ ClipEndBottom (line);
+ return true;
+
+ case 0x85:
+ ClipStartTop (line);
+ if (line.start.x < m_clipMin.x)
+ return false;
+ ClipEndLeft (line);
+ if (line.end.y > m_clipMax.y)
+ ClipEndBottom (line);
+ return true;
+
+ case 0x86:
+ ClipStartTop (line);
+ if (line.start.x > m_clipMax.x)
+ return false;
+ ClipEndRight (line);
+ if (line.end.y > m_clipMax.y)
+ ClipEndBottom (line);
+ return true;
+
+ // top-left
+ case 0x90:
+ ClipStartLeft (line);
+ if (line.start.y < m_clipMin.y)
+ ClipStartTop (line);
+ return true;
+
+ case 0x92:
+ ClipEndRight (line);
+ if (line.end.y < m_clipMin.y)
+ return false;
+ ClipStartTop (line);
+ if (line.start.x < m_clipMin.x)
+ ClipStartLeft (line);
+ return true;
+
+ case 0x94:
+ ClipEndBottom (line);
+ if (line.end.x < m_clipMin.x)
+ return false;
+ ClipStartLeft (line);
+ if (line.start.y < m_clipMin.y)
+ ClipStartTop (line);
+ return true;
+
+ case 0x96:
+ ClipStartLeft (line);
+ if (line.start.y > m_clipMax.y)
+ return false;
+ ClipEndRight (line);
+ if (line.end.y < m_clipMin.y)
+ return false;
+ if (line.start.y < m_clipMin.y)
+ ClipStartTop (line);
+ if (line.end.y > m_clipMax.y)
+ ClipEndBottom (line);
+ return true;
+
+ // top-right
+ case 0xA0:
+ ClipStartRight (line);
+ if (line.start.y < m_clipMin.y)
+ ClipStartTop (line);
+ return true;
+
+ case 0xA1:
+ ClipEndLeft (line);
+ if (line.end.y < m_clipMin.y)
+ return false;
+ ClipStartTop (line);
+ if (line.start.x > m_clipMax.x)
+ ClipStartRight (line);
+ return true;
+
+ case 0xA4:
+ ClipEndBottom (line);
+ if (line.end.x > m_clipMax.x)
+ return false;
+ ClipStartRight (line);
+ if (line.start.y < m_clipMin.y)
+ ClipStartTop (line);
+ return true;
+
+ case 0xA5:
+ ClipEndLeft (line);
+ if (line.end.y < m_clipMin.y)
+ return false;
+ ClipStartRight (line);
+ if (line.start.y > m_clipMax.y)
+ return false;
+ if (line.end.y > m_clipMax.y)
+ ClipEndBottom (line);
+ if (line.start.y < m_clipMin.y)
+ ClipStartTop (line);
+ return true;
+ }
+
+ return false;
+ }
+ };
+}
+
+void
+PyViz::LineClipping (double boundsX1, double boundsY1, double boundsX2, double boundsY2,
+ double &lineX1, double &lineY1, double &lineX2, double &lineY2)
+{
+ FastClipping::Vector2 clipMin = {boundsX1, boundsY1}, clipMax = {boundsX2, boundsY2};
+ FastClipping::Line line = { { lineX1, lineY1 }, { lineX2, lineY2 }, (lineX2-lineX1), (lineY2-lineY1) };
+
+ FastClipping clipper (clipMin, clipMax);
+ clipper.ClipLine (line);
+ lineX1 = line.start.x;
+ lineX2 = line.end.x;
+ lineY1 = line.start.y;
+ lineY2 = line.end.y;
+}
+
+
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/model/pyviz.h Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,225 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2008 INESC Porto
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * C++ helper functions for use by the python visualizer (for things
+ * Python is too slow at).
+ *
+ * Author: Gustavo Carneiro <gjc@inescporto.pt>
+ */
+#ifndef NS3_PYVIZ_H
+#define NS3_PYVIZ_H
+
+#include "ns3/nstime.h"
+#include "ns3/event-id.h"
+#include "ns3/node.h"
+#include "ns3/channel.h"
+#include "ns3/packet.h"
+#include "ns3/mac48-address.h"
+#include "ns3/ipv4-header.h"
+#include "ns3/ipv4-l3-protocol.h"
+
+#include <map>
+#include <set>
+
+namespace ns3 {
+
+/**
+ * \brief helper class to be used by the visualizer
+ * \internal
+ *
+ * This class is not meant to be used by simulations. It is only
+ * meant to be used by the visualizer tool (PyViz). The only reason
+ * it is public is because Python bindings for it are needed,
+ * otherwise it should be considered private.
+ **/
+class PyViz
+{
+public:
+ PyViz ();
+ ~PyViz ();
+
+ void RegisterDropTracePath (std::string const &tracePath);
+
+ void RegisterCsmaLikeDevice (std::string const &deviceTypeName);
+ void RegisterWifiLikeDevice (std::string const &deviceTypeName);
+ void RegisterPointToPointLikeDevice (std::string const &deviceTypeName);
+
+ // Run simulation until a given (simulated, absolute) time is reached
+ void SimulatorRunUntil (Time time);
+
+ static void Pause (std::string const &message);
+ std::vector<std::string> GetPauseMessages () const;
+
+ struct TransmissionSample
+ {
+ Ptr<Node> transmitter;
+ Ptr<Node> receiver; // NULL if broadcast
+ Ptr<Channel> channel;
+ uint32_t bytes;
+ };
+ typedef std::vector<TransmissionSample> TransmissionSampleList;
+ TransmissionSampleList GetTransmissionSamples () const;
+
+ struct PacketDropSample
+ {
+ Ptr<Node> transmitter;
+ uint32_t bytes;
+ };
+ typedef std::vector<PacketDropSample> PacketDropSampleList;
+ PacketDropSampleList GetPacketDropSamples () const;
+
+
+ struct PacketSample
+ {
+ Time time;
+ Ptr<Packet> packet;
+ Ptr<NetDevice> device;
+ };
+ struct TxPacketSample : PacketSample
+ {
+ Mac48Address to;
+ };
+ struct RxPacketSample : PacketSample
+ {
+ Mac48Address from;
+ };
+
+ struct LastPacketsSample
+ {
+ std::vector<RxPacketSample> lastReceivedPackets;
+ std::vector<TxPacketSample> lastTransmittedPackets;
+ std::vector<PacketSample> lastDroppedPackets;
+ };
+ LastPacketsSample GetLastPackets (uint32_t nodeId) const;
+
+
+ void SetNodesOfInterest (std::set<uint32_t> nodes);
+
+ struct NetDeviceStatistics
+ {
+ NetDeviceStatistics () : transmittedBytes (0), receivedBytes (0),
+ transmittedPackets (0), receivedPackets (0) {}
+ uint64_t transmittedBytes;
+ uint64_t receivedBytes;
+ uint32_t transmittedPackets;
+ uint32_t receivedPackets;
+ };
+
+ struct NodeStatistics
+ {
+ uint32_t nodeId;
+ std::vector<NetDeviceStatistics> statistics;
+ };
+
+ std::vector<NodeStatistics> GetNodesStatistics () const;
+
+ enum PacketCaptureMode {
+ PACKET_CAPTURE_DISABLED=1, // packet capture is disabled
+ PACKET_CAPTURE_FILTER_HEADERS_OR, // packet capture if any of the indicated headers is present
+ PACKET_CAPTURE_FILTER_HEADERS_AND, // packet capture if all of the indicated headers are present
+ };
+
+ struct PacketCaptureOptions
+ {
+ std::set<TypeId> headers;
+ uint32_t numLastPackets;
+ PacketCaptureMode mode;
+ };
+
+ void SetPacketCaptureOptions (uint32_t nodeId, PacketCaptureOptions options);
+
+
+ // Yes, I know, this is just a utility function, not really related to the class in any way.
+
+ // -#- @lineX1(direction=inout); @lineY1(direction=inout); @lineX2(direction=inout); @lineY2(direction=inout) -#-
+ static void LineClipping (double boundsX1, double boundsY1, double boundsX2, double boundsY2, double &lineX1, double &lineY1, double &lineX2, double &lineY2); // don't break this line or pybindgen will not be able to pick up the above annotation :(
+
+
+private:
+
+ bool GetPacketCaptureOptions (uint32_t nodeId, const PacketCaptureOptions **outOptions) const;
+ static bool FilterPacket (Ptr<const Packet> packet, const PacketCaptureOptions &options);
+
+
+ typedef std::pair<Ptr<Channel>, uint32_t> TxRecordKey;
+
+ struct TxRecordValue
+ {
+ Time time;
+ Ptr<Node> srcNode;
+ bool isBroadcast;
+ };
+
+ struct TransmissionSampleKey
+ {
+ bool operator < (TransmissionSampleKey const &other) const;
+ bool operator == (TransmissionSampleKey const &other) const;
+ Ptr<Node> transmitter;
+ Ptr<Node> receiver; // NULL if broadcast
+ Ptr<Channel> channel;
+ };
+
+ struct TransmissionSampleValue
+ {
+ uint32_t bytes;
+ };
+
+ // data
+ std::map<uint32_t, PacketCaptureOptions> m_packetCaptureOptions;
+ std::vector<std::string> m_pauseMessages;
+ std::map<TxRecordKey, TxRecordValue> m_txRecords;
+ std::map<TransmissionSampleKey, TransmissionSampleValue> m_transmissionSamples;
+ std::map<Ptr<Node>, uint32_t> m_packetDrops;
+ std::set<uint32_t> m_nodesOfInterest; // list of node IDs whose transmissions will be monitored
+ std::map<uint32_t, Time> m_packetsOfInterest; // list of packet UIDs that will be monitored
+ std::map<uint32_t, LastPacketsSample> m_lastPackets;
+ std::map<uint32_t, std::vector<NetDeviceStatistics> > m_nodesStatistics;
+
+ // Trace callbacks
+ void TraceNetDevTxCommon (std::string const &context, Ptr<const Packet> packet, Mac48Address const &destination);
+ void TraceNetDevRxCommon (std::string const &context, Ptr<const Packet> packet, Mac48Address const &source);
+
+ void TraceNetDevTxWifi (std::string context, Ptr<const Packet> packet);
+ void TraceNetDevRxWifi (std::string context, Ptr<const Packet> packet);
+
+ void TraceDevQueueDrop (std::string context, Ptr<const Packet> packet);
+ void TraceIpv4Drop (std::string context, ns3::Ipv4Header const &hdr, Ptr<const Packet> packet,
+ ns3::Ipv4L3Protocol::DropReason reason, Ptr<Ipv4> dummy_ipv4, uint32_t interface);
+
+ void TraceNetDevTxCsma (std::string context, Ptr<const Packet> packet);
+ void TraceNetDevRxCsma (std::string context, Ptr<const Packet> packet);
+ void TraceNetDevPromiscRxCsma (std::string context, Ptr<const Packet> packet);
+
+ void TraceNetDevTxPointToPoint (std::string context, Ptr<const Packet> packet);
+ void TraceNetDevRxPointToPoint (std::string context, Ptr<const Packet> packet);
+
+ void TraceNetDevTxWimax (std::string context, Ptr<const Packet> packet, Mac48Address const &destination);
+ void TraceNetDevRxWimax (std::string context, Ptr<const Packet> packet, Mac48Address const &source);
+
+ inline NetDeviceStatistics & FindNetDeviceStatistics (int node, int interface);
+
+ void DoPause (std::string const &message);
+
+ bool m_stop;
+ EventId m_stopCallbackEvent;
+ void CallbackStopSimulation ();
+};
+
+
+}
+
+#endif /* NS3_PYVIZ_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/model/visual-simulator-impl.cc Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,228 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2010 Gustavo Carneiro
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Gustavo Carneiro <gjcarneiro@gmail.com> <gjc@inescporto.pt>
+ */
+#include <Python.h>
+#include "visual-simulator-impl.h"
+#include "ns3/default-simulator-impl.h"
+#include "ns3/log.h"
+
+NS_LOG_COMPONENT_DEFINE ("VisualSimulatorImpl");
+
+namespace ns3 {
+
+
+
+NS_OBJECT_ENSURE_REGISTERED (VisualSimulatorImpl);
+
+namespace
+{
+ ObjectFactory
+ GetDefaultSimulatorImplFactory ()
+ {
+ ObjectFactory factory;
+ factory.SetTypeId (DefaultSimulatorImpl::GetTypeId ());
+ return factory;
+ }
+}
+
+
+TypeId
+VisualSimulatorImpl::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::VisualSimulatorImpl")
+ .SetParent<SimulatorImpl> ()
+ .AddConstructor<VisualSimulatorImpl> ()
+ .AddAttribute ("SimulatorImplFactory",
+ "Factory for the underlying simulator implementation used by the visualizer.",
+ ObjectFactoryValue (GetDefaultSimulatorImplFactory ()),
+ MakeObjectFactoryAccessor (&VisualSimulatorImpl::m_simulatorImplFactory),
+ MakeObjectFactoryChecker ())
+ ;
+ return tid;
+}
+
+
+VisualSimulatorImpl::VisualSimulatorImpl ()
+{
+}
+
+VisualSimulatorImpl::~VisualSimulatorImpl ()
+{}
+
+void
+VisualSimulatorImpl::DoDispose (void)
+{
+ if (m_simulator)
+ {
+ m_simulator->Dispose ();
+ m_simulator = NULL;
+ }
+ SimulatorImpl::DoDispose ();
+}
+
+void
+VisualSimulatorImpl::NotifyConstructionCompleted ()
+{
+ m_simulator = m_simulatorImplFactory.Create<SimulatorImpl> ();
+}
+
+
+void
+VisualSimulatorImpl::Destroy ()
+{
+ m_simulator->Destroy ();
+}
+
+void
+VisualSimulatorImpl::SetScheduler (ObjectFactory schedulerFactory)
+{
+ m_simulator->SetScheduler (schedulerFactory);
+}
+
+// System ID for non-distributed simulation is always zero
+uint32_t
+VisualSimulatorImpl::GetSystemId (void) const
+{
+ return m_simulator->GetSystemId ();
+}
+
+bool
+VisualSimulatorImpl::IsFinished (void) const
+{
+ return m_simulator->IsFinished ();
+}
+
+Time
+VisualSimulatorImpl::Next (void) const
+{
+ return m_simulator->Next ();
+}
+
+void
+VisualSimulatorImpl::Run (void)
+{
+ if (!Py_IsInitialized ())
+ {
+ const char *argv[] = {"python", NULL};
+ Py_Initialize();
+ PySys_SetArgv(1, (char**) argv);
+ }
+ PyRun_SimpleString(
+ "import visualizer\n"
+ "visualizer.start();\n"
+ );
+}
+
+void
+VisualSimulatorImpl::RunOneEvent (void)
+{
+ m_simulator->RunOneEvent ();
+}
+
+void
+VisualSimulatorImpl::Stop (void)
+{
+ m_simulator->Stop ();
+}
+
+void
+VisualSimulatorImpl::Stop (Time const &time)
+{
+ m_simulator->Stop (time);
+}
+
+//
+// Schedule an event for a _relative_ time in the future.
+//
+EventId
+VisualSimulatorImpl::Schedule (Time const &time, EventImpl *event)
+{
+ return m_simulator->Schedule (time, event);
+}
+
+void
+VisualSimulatorImpl::ScheduleWithContext (uint32_t context, Time const &time, EventImpl *event)
+{
+ m_simulator->ScheduleWithContext (context, time, event);
+}
+
+EventId
+VisualSimulatorImpl::ScheduleNow (EventImpl *event)
+{
+ return m_simulator->ScheduleNow (event);
+}
+
+EventId
+VisualSimulatorImpl::ScheduleDestroy (EventImpl *event)
+{
+ return m_simulator->ScheduleDestroy (event);
+}
+
+Time
+VisualSimulatorImpl::Now (void) const
+{
+ return m_simulator->Now ();
+}
+
+Time
+VisualSimulatorImpl::GetDelayLeft (const EventId &id) const
+{
+ return m_simulator->GetDelayLeft (id);
+}
+
+void
+VisualSimulatorImpl::Remove (const EventId &id)
+{
+ m_simulator->Remove (id);
+}
+
+void
+VisualSimulatorImpl::Cancel (const EventId &id)
+{
+ m_simulator->Cancel (id);
+}
+
+bool
+VisualSimulatorImpl::IsExpired (const EventId &ev) const
+{
+ return m_simulator->IsExpired (ev);
+}
+
+Time
+VisualSimulatorImpl::GetMaximumSimulationTime (void) const
+{
+ return m_simulator->GetMaximumSimulationTime ();
+}
+
+uint32_t
+VisualSimulatorImpl::GetContext (void) const
+{
+ return m_simulator->GetContext ();
+}
+
+void
+VisualSimulatorImpl::RunRealSimulator (void)
+{
+ m_simulator->Run ();
+}
+
+
+} // namespace ns3
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/model/visual-simulator-impl.h Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,82 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2010 Gustavo Carneiro
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Gustavo Carneiro <gjcarneiro@gmail.com>
+ */
+
+#ifndef VISUAL_SIMULATOR_IMPL_H
+#define VISUAL_SIMULATOR_IMPL_H
+
+#include "ns3/simulator-impl.h"
+
+namespace ns3 {
+
+
+/**
+ * \brief A replacement simulator that starts the visualizer
+ * \internal
+ *
+ * To use this class, run any ns-3 simulation with the command-line
+ * argument --SimulatorImplementationType=ns3::VisualSimulatorImpl.
+ * This causes the visualizer (PyViz) to start automatically.
+ **/
+class VisualSimulatorImpl : public SimulatorImpl
+{
+public:
+ static TypeId GetTypeId (void);
+
+ VisualSimulatorImpl ();
+ ~VisualSimulatorImpl ();
+
+ virtual void Destroy ();
+ virtual bool IsFinished (void) const;
+ virtual Time Next (void) const;
+ virtual void Stop (void);
+ virtual void Stop (Time const &time);
+ virtual EventId Schedule (Time const &time, EventImpl *event);
+ virtual void ScheduleWithContext (uint32_t context, Time const &time, EventImpl *event);
+ virtual EventId ScheduleNow (EventImpl *event);
+ virtual EventId ScheduleDestroy (EventImpl *event);
+ virtual void Remove (const EventId &ev);
+ virtual void Cancel (const EventId &ev);
+ virtual bool IsExpired (const EventId &ev) const;
+ virtual void Run (void);
+ virtual void RunOneEvent (void);
+ virtual Time Now (void) const;
+ virtual Time GetDelayLeft (const EventId &id) const;
+ virtual Time GetMaximumSimulationTime (void) const;
+ virtual void SetScheduler (ObjectFactory schedulerFactory);
+ virtual uint32_t GetSystemId (void) const;
+ virtual uint32_t GetContext (void) const;
+
+ /// calls Run() in the wrapped simulator
+ void RunRealSimulator (void);
+
+protected:
+ void DoDispose ();
+ void NotifyConstructionCompleted (void);
+
+private:
+ Ptr<SimulatorImpl> GetSim ();
+ Ptr<SimulatorImpl> m_simulator;
+ ObjectFactory m_simulatorImplFactory;
+
+};
+
+} // namespace ns3
+
+#endif /* DEFAULT_SIMULATOR_IMPL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/model/visualizer-ideas.txt Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,16 @@
+- Add an Attribute browser plugin, simililar to Mathieu's GtkConfigStore;
+ - Right click on a node -> Show Attributes;
+ - Allow editing attributes too;
+- List of all nodes, navigator;
+- Represent individual NetDevices in Nodes;
+- Colorize flows; possible approaches:
+ - Apply color based on hash function of ethertype, IP packet type, L4 destination port;
+ - Programmatically marked flows;
+ - Packet tags?
+ - Present a GUI to show applications and set color for each one;
+ - Problems:
+ > How about multiple flows? How to represent them simultaneously?
+- Track down a Gtk+ bug preventing tooltips from working correctly with large zoom levels;
+- Possibly look for embedding an ipython shell as a widget inside the
+ main window: http://ipython.scipy.org/moin/Cookbook/EmbeddingInGTK
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/__init__.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,3 @@
+
+from core import start, register_plugin, set_bounds, add_initialization_hook
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/base.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,107 @@
+import ns3
+import gobject
+import os.path
+import sys
+
+PIXELS_PER_METER = 3.0 # pixels-per-meter, at 100% zoom level
+
+class PyVizObject(gobject.GObject):
+ __gtype_name__ = "PyVizObject"
+
+ def tooltip_query(self, tooltip):
+ tooltip.set_text("TODO: tooltip for %r" % self)
+
+class Link(PyVizObject):
+ pass
+
+
+class InformationWindow(object):
+ def update(self):
+ raise NotImplementedError
+
+class NetDeviceTraits(object):
+ def __init__(self, is_wireless=None, is_virtual=False):
+ assert is_virtual or is_wireless is not None
+ self.is_wireless = is_wireless
+ self.is_virtual = is_virtual
+
+netdevice_traits = {
+ ns3.PointToPointNetDevice: NetDeviceTraits(is_wireless=False),
+ ns3.CsmaNetDevice: NetDeviceTraits(is_wireless=False),
+ ns3.WifiNetDevice: NetDeviceTraits(is_wireless=True),
+ ns3.BridgeNetDevice: NetDeviceTraits(is_virtual=True),
+ ns3.LoopbackNetDevice: NetDeviceTraits(is_virtual=True, is_wireless=False),
+ ns3.MeshPointDevice: NetDeviceTraits(is_virtual=True),
+ ns3.SubscriberStationNetDevice: NetDeviceTraits(is_wireless=True),
+ ns3.BaseStationNetDevice: NetDeviceTraits(is_wireless=True),
+}
+
+def lookup_netdevice_traits(class_type):
+ try:
+ return netdevice_traits[class_type]
+ except KeyError:
+ sys.stderr.write("WARNING: no NetDeviceTraits registered for device type %r; "
+ "I will assume this is a non-virtual wireless device, "
+ "but you should edit %r, variable 'netdevice_traits',"
+ " to make sure.\n" % (class_type.__name__, __file__))
+ t = NetDeviceTraits(is_virtual=False, is_wireless=True)
+ netdevice_traits[class_type] = t
+ return t
+
+def transform_distance_simulation_to_canvas(d):
+ return d*PIXELS_PER_METER
+
+def transform_point_simulation_to_canvas(x, y):
+ return x*PIXELS_PER_METER, y*PIXELS_PER_METER
+
+def transform_distance_canvas_to_simulation(d):
+ return d/PIXELS_PER_METER
+
+def transform_point_canvas_to_simulation(x, y):
+ return x/PIXELS_PER_METER, y/PIXELS_PER_METER
+
+
+
+
+plugins = []
+plugin_modules = {}
+
+def register_plugin(plugin_init_func, plugin_name=None, plugin_module=None):
+ """
+ Register a plugin.
+
+ @param plugin: a callable object that will be invoked whenever a
+ Visualizer object is created, like this: plugin(visualizer)
+ """
+ assert callable(plugin_init_func)
+ plugins.append(plugin_init_func)
+ if plugin_module is not None:
+ plugin_modules[plugin_name] = plugin_module
+
+plugins_loaded = False
+def load_plugins():
+ global plugins_loaded
+ if plugins_loaded:
+ return
+ plugins_loaded = True
+ plugins_dir = os.path.join(os.path.dirname(__file__), 'plugins')
+ old_path = list(sys.path)
+ sys.path.insert(0, plugins_dir)
+ for filename in os.listdir(plugins_dir):
+ name, ext = os.path.splitext(filename)
+ if ext != '.py':
+ continue
+ try:
+ plugin_module = __import__(name)
+ except ImportError, ex:
+ print >> sys.stderr, "Could not load plugin %r: %s" % (filename, str(ex))
+ continue
+ try:
+ plugin_func = plugin_module.register
+ except AttributeError:
+ print >> sys.stderr, "Plugin %r has no 'register' function" % name
+ else:
+ #print >> sys.stderr, "Plugin %r registered" % name
+ register_plugin(plugin_func, name, plugin_module)
+ sys.path = old_path
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/core.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,1470 @@
+# -*- Mode: python; coding: utf-8 -*-
+from __future__ import division
+#from __future__ import with_statement
+
+LAYOUT_ALGORITHM = 'neato' # ['neato'|'dot'|'twopi'|'circo'|'fdp'|'nop']
+REPRESENT_CHANNELS_AS_NODES = 1
+DEFAULT_NODE_SIZE = 3.0 # default node size in meters
+DEFAULT_TRANSMISSIONS_MEMORY = 5 # default number of of past intervals whose transmissions are remembered
+BITRATE_FONT_SIZE = 10
+
+# internal constants, normally not meant to be changed
+SAMPLE_PERIOD = 0.1
+PRIORITY_UPDATE_MODEL = -100
+PRIORITY_UPDATE_VIEW = 200
+
+import platform
+if platform.system() == "Windows":
+ SHELL_FONT = "Lucida Console 9"
+else:
+ SHELL_FONT = "Luxi Mono 10"
+
+
+import ns3
+import math
+import os
+import sys
+import gobject
+import time
+
+try:
+ import pygraphviz
+ import gtk
+ import pango
+ import goocanvas
+ import cairo
+ import threading
+ import hud
+ #import time
+ import cairo
+ from higcontainer import HIGContainer
+ gobject.threads_init()
+ try:
+ import svgitem
+ except ImportError:
+ svgitem = None
+except ImportError, _import_error:
+ import dummy_threading as threading
+else:
+ _import_error = None
+
+try:
+ import ipython_view
+except ImportError:
+ ipython_view = None
+
+from base import InformationWindow, PyVizObject, Link, lookup_netdevice_traits, PIXELS_PER_METER
+from base import transform_distance_simulation_to_canvas, transform_point_simulation_to_canvas
+from base import transform_distance_canvas_to_simulation, transform_point_canvas_to_simulation
+from base import load_plugins, register_plugin, plugins
+
+PI_OVER_2 = math.pi/2
+PI_TIMES_2 = math.pi*2
+
+class Node(PyVizObject):
+
+ __gsignals__ = {
+
+ # signal emitted whenever a tooltip is about to be shown for the node
+ # the first signal parameter is a python list of strings, to which information can be appended
+ 'query-extra-tooltip-info': (gobject.SIGNAL_RUN_LAST, None, (object,)),
+
+ }
+
+ def __init__(self, visualizer, node_index):
+ super(Node, self).__init__()
+
+ self.visualizer = visualizer
+ self.node_index = node_index
+ self.canvas_item = goocanvas.Ellipse()
+ self.canvas_item.set_data("pyviz-object", self)
+ self.links = []
+ self._has_mobility = None
+ self._selected = False
+ self._highlighted = False
+ self._color = 0x808080ff
+ self._size = DEFAULT_NODE_SIZE
+ self.canvas_item.connect("enter-notify-event", self.on_enter_notify_event)
+ self.canvas_item.connect("leave-notify-event", self.on_leave_notify_event)
+ self.menu = None
+ self.svg_item = None
+ self.svg_align_x = None
+ self.svg_align_y = None
+ self._label = None
+ self._label_canvas_item = None
+
+ self._update_appearance() # call this last
+
+ def set_svg_icon(self, file_base_name, width=None, height=None, align_x=0.5, align_y=0.5):
+ """
+ Set a background SVG icon for the node.
+
+ @param file_base_name: base file name, including .svg
+ extension, of the svg file. Place the file in the folder
+ src/contrib/visualizer/resource.
+
+ @param width: scale to the specified width, in meters
+ @param width: scale to the specified height, in meters
+
+ @param align_x: horizontal alignment of the icon relative to
+ the node position, from 0 (icon fully to the left of the node)
+ to 1.0 (icon fully to the right of the node)
+
+ @param align_y: vertical alignment of the icon relative to the
+ node position, from 0 (icon fully to the top of the node) to
+ 1.0 (icon fully to the bottom of the node)
+
+ """
+ if width is None and height is None:
+ raise ValueError("either width or height must be given")
+ rsvg_handle = svgitem.rsvg_handle_factory(file_base_name)
+ x = self.canvas_item.props.center_x
+ y = self.canvas_item.props.center_y
+ self.svg_item = svgitem.SvgItem(x, y, rsvg_handle)
+ self.svg_item.props.parent = self.visualizer.canvas.get_root_item()
+ self.svg_item.props.pointer_events = 0
+ self.svg_item.lower(None)
+ self.svg_item.props.visibility = goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD
+ if width is not None:
+ self.svg_item.props.width = transform_distance_simulation_to_canvas(width)
+ if height is not None:
+ self.svg_item.props.height = transform_distance_simulation_to_canvas(height)
+
+ #threshold1 = 10.0/self.svg_item.props.height
+ #threshold2 = 10.0/self.svg_item.props.width
+ #self.svg_item.props.visibility_threshold = min(threshold1, threshold2)
+
+ self.svg_align_x = align_x
+ self.svg_align_y = align_y
+ self._update_svg_position(x, y)
+ self._update_appearance()
+
+ def set_label(self, label):
+ assert isinstance(label, basestring)
+ self._label = label
+ self._update_appearance()
+
+ def _update_svg_position(self, x, y):
+ w = self.svg_item.width
+ h = self.svg_item.height
+ self.svg_item.set_properties(x=(x - (1-self.svg_align_x)*w),
+ y=(y - (1-self.svg_align_y)*h))
+
+
+ def tooltip_query(self, tooltip):
+ self.visualizer.simulation.lock.acquire()
+ try:
+ ns3_node = ns3.NodeList.GetNode(self.node_index)
+ ipv4 = ns3_node.GetObject(ns3.Ipv4.GetTypeId())
+ lines = ['<b><u>Node %i</u></b>' % self.node_index]
+ lines.append('')
+
+ self.emit("query-extra-tooltip-info", lines)
+
+ mob = ns3_node.GetObject(ns3.MobilityModel.GetTypeId())
+ if mob is not None:
+ lines.append(' <b>Mobility Model</b>: %s' % mob.GetInstanceTypeId().GetName())
+
+ for devI in range(ns3_node.GetNDevices()):
+ lines.append('')
+ lines.append(' <u>NetDevice %i:</u>' % devI)
+ dev = ns3_node.GetDevice(devI)
+ name = ns3.Names.FindName(dev)
+ if name:
+ lines.append(' <b>Name:</b> %s' % name)
+ devname = dev.GetInstanceTypeId().GetName()
+ lines.append(' <b>Type:</b> %s' % devname)
+ if ipv4 is not None:
+ ipv4_idx = ipv4.GetInterfaceForDevice(dev)
+ if ipv4_idx != -1:
+ addresses = [
+ '%s/%s' % (ipv4.GetAddress(ipv4_idx, i).GetLocal(),
+ ipv4.GetAddress(ipv4_idx, i).GetMask())
+ for i in range(ipv4.GetNAddresses(ipv4_idx))]
+ lines.append(' <b>IPv4 Addresses:</b> %s' % '; '.join(addresses))
+
+ lines.append(' <b>MAC Address:</b> %s' % (dev.GetAddress(),))
+
+ tooltip.set_markup('\n'.join(lines))
+ finally:
+ self.visualizer.simulation.lock.release()
+
+ def on_enter_notify_event(self, view, target, event):
+ self.highlighted = True
+ def on_leave_notify_event(self, view, target, event):
+ self.highlighted = False
+
+ def _set_selected(self, value):
+ self._selected = value
+ self._update_appearance()
+ def _get_selected(self):
+ return self._selected
+ selected = property(_get_selected, _set_selected)
+
+ def _set_highlighted(self, value):
+ self._highlighted = value
+ self._update_appearance()
+ def _get_highlighted(self):
+ return self._highlighted
+ highlighted = property(_get_highlighted, _set_highlighted)
+
+ def set_size(self, size):
+ self._size = size
+ self._update_appearance()
+
+ def _update_appearance(self):
+ """Update the node aspect to reflect the selected/highlighted state"""
+
+ size = transform_distance_simulation_to_canvas(self._size)
+ if self.svg_item is not None:
+ alpha = 0x80
+ else:
+ alpha = 0xff
+ fill_color_rgba = (self._color & 0xffffff00) | alpha
+ self.canvas_item.set_properties(radius_x=size, radius_y=size,
+ fill_color_rgba=fill_color_rgba)
+ if self._selected:
+ line_width = size*.3
+ else:
+ line_width = size*.15
+ if self.highlighted:
+ stroke_color = 'yellow'
+ else:
+ stroke_color = 'black'
+ self.canvas_item.set_properties(line_width=line_width, stroke_color=stroke_color)
+
+ if self._label is not None:
+ if self._label_canvas_item is None:
+ self._label_canvas_item = goocanvas.Text(visibility_threshold=0.5,
+ font="Sans Serif 10",
+ fill_color_rgba=0x808080ff,
+ alignment=pango.ALIGN_CENTER,
+ anchor=gtk.ANCHOR_N,
+ parent=self.visualizer.canvas.get_root_item(),
+ pointer_events=0)
+ self._label_canvas_item.lower(None)
+
+ self._label_canvas_item.set_properties(visibility=goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD,
+ text=self._label)
+ self._update_position()
+
+ def set_position(self, x, y):
+ self.canvas_item.set_property("center_x", x)
+ self.canvas_item.set_property("center_y", y)
+ if self.svg_item is not None:
+ self._update_svg_position(x, y)
+
+ for link in self.links:
+ link.update_points()
+
+ if self._label_canvas_item is not None:
+ self._label_canvas_item.set_properties(x=x, y=(y+self._size*3))
+
+ def get_position(self):
+ return (self.canvas_item.get_property("center_x"), self.canvas_item.get_property("center_y"))
+
+ def _update_position(self):
+ x, y = self.get_position()
+ self.set_position(x, y)
+
+ def set_color(self, color):
+ if isinstance(color, str):
+ color = gtk.gdk.color_parse(color)
+ color = ((color.red>>8) << 24) | ((color.green>>8) << 16) | ((color.blue>>8) << 8) | 0xff
+ self._color = color
+ self._update_appearance()
+
+ def add_link(self, link):
+ assert isinstance(link, Link)
+ self.links.append(link)
+
+ def remove_link(self, link):
+ assert isinstance(link, Link)
+ self.links.remove(link)
+
+ @property
+ def has_mobility(self):
+ if self._has_mobility is None:
+ node = ns3.NodeList.GetNode(self.node_index)
+ mobility = node.GetObject(ns3.MobilityModel.GetTypeId())
+ self._has_mobility = (mobility is not None)
+ return self._has_mobility
+
+
+class Channel(PyVizObject):
+ def __init__(self, channel):
+ self.channel = channel
+ self.canvas_item = goocanvas.Ellipse(radius_x=30, radius_y=30,
+ fill_color="white",
+ stroke_color="grey", line_width=2.0,
+ line_dash=goocanvas.LineDash([10.0, 10.0 ]),
+ visibility=goocanvas.ITEM_VISIBLE)
+ self.canvas_item.set_data("pyviz-object", self)
+ self.links = []
+
+ def set_position(self, x, y):
+ self.canvas_item.set_property("center_x", x)
+ self.canvas_item.set_property("center_y", y)
+
+ for link in self.links:
+ link.update_points()
+
+ def get_position(self):
+ return (self.canvas_item.get_property("center_x"), self.canvas_item.get_property("center_y"))
+
+
+class WiredLink(Link):
+ def __init__(self, node1, node2):
+ assert isinstance(node1, Node)
+ assert isinstance(node2, (Node, Channel))
+ self.node1 = node1
+ self.node2 = node2
+ self.canvas_item = goocanvas.Path(line_width=1.0, stroke_color="black")
+ self.canvas_item.set_data("pyviz-object", self)
+ self.node1.links.append(self)
+ self.node2.links.append(self)
+
+ def update_points(self):
+ pos1_x, pos1_y = self.node1.get_position()
+ pos2_x, pos2_y = self.node2.get_position()
+ self.canvas_item.set_property("data", "M %r %r L %r %r" % (pos1_x, pos1_y, pos2_x, pos2_y))
+
+
+
+class SimulationThread(threading.Thread):
+ def __init__(self, viz):
+ super(SimulationThread, self).__init__()
+ assert isinstance(viz, Visualizer)
+ self.viz = viz # Visualizer object
+ self.lock = threading.Lock()
+ self.go = threading.Event()
+ self.go.clear()
+ self.target_time = 0 # in seconds
+ self.quit = False
+ self.sim_helper = ns3.PyViz()
+ self.pause_messages = []
+
+ def set_nodes_of_interest(self, nodes):
+ self.lock.acquire()
+ try:
+ self.sim_helper.SetNodesOfInterest(nodes)
+ finally:
+ self.lock.release()
+
+ def run(self):
+ while not self.quit:
+ #print "sim: Wait for go"
+ self.go.wait() # wait until the main (view) thread gives us the go signal
+ self.go.clear()
+ if self.quit:
+ break
+ #self.go.clear()
+ #print "sim: Acquire lock"
+ self.lock.acquire()
+ try:
+ if 0:
+ if ns3.Simulator.IsFinished():
+ self.viz.play_button.set_sensitive(False)
+ break
+ #print "sim: Current time is %f; Run until: %f" % (ns3.Simulator.Now ().GetSeconds (), self.target_time)
+ #if ns3.Simulator.Now ().GetSeconds () > self.target_time:
+ # print "skipping, model is ahead of view!"
+ self.sim_helper.SimulatorRunUntil(ns3.Seconds(self.target_time))
+ #print "sim: Run until ended at current time: ", ns3.Simulator.Now ().GetSeconds ()
+ self.pause_messages.extend(self.sim_helper.GetPauseMessages())
+ gobject.idle_add(self.viz.update_model, priority=PRIORITY_UPDATE_MODEL)
+ #print "sim: Run until: ", self.target_time, ": finished."
+ finally:
+ self.lock.release()
+ #print "sim: Release lock, loop."
+
+# enumeration
+class ShowTransmissionsMode(object):
+ __slots__ = []
+ShowTransmissionsMode.ALL = ShowTransmissionsMode()
+ShowTransmissionsMode.NONE = ShowTransmissionsMode()
+ShowTransmissionsMode.SELECTED = ShowTransmissionsMode()
+
+class Visualizer(gobject.GObject):
+ INSTANCE = None
+
+ if _import_error is None:
+ __gsignals__ = {
+
+ # signal emitted whenever a right-click-on-node popup menu is being constructed
+ 'populate-node-menu': (gobject.SIGNAL_RUN_LAST, None, (object, gtk.Menu,)),
+
+ # signal emitted after every simulation period (SAMPLE_PERIOD seconds of simulated time)
+ # the simulation lock is acquired while the signal is emitted
+ 'simulation-periodic-update': (gobject.SIGNAL_RUN_LAST, None, ()),
+
+ # signal emitted right after the topology is scanned
+ 'topology-scanned': (gobject.SIGNAL_RUN_LAST, None, ()),
+
+ # signal emitted when it's time to update the view objects
+ 'update-view': (gobject.SIGNAL_RUN_LAST, None, ()),
+
+ }
+
+ def __init__(self):
+ assert Visualizer.INSTANCE is None
+ Visualizer.INSTANCE = self
+ super(Visualizer, self).__init__()
+ self.nodes = {} # node index -> Node
+ self.channels = {} # id(ns3.Channel) -> Channel
+ self.window = None # toplevel window
+ self.canvas = None # goocanvas.Canvas
+ self.time_label = None # gtk.Label
+ self.play_button = None # gtk.ToggleButton
+ self.zoom = None # gtk.Adjustment
+ self._scrolled_window = None # gtk.ScrolledWindow
+
+ self.links_group = goocanvas.Group()
+ self.channels_group = goocanvas.Group()
+ self.nodes_group = goocanvas.Group()
+
+ self._update_timeout_id = None
+ self.simulation = SimulationThread(self)
+ self.selected_node = None # node currently selected
+ self.speed = 1.0
+ self.information_windows = []
+ self._transmission_arrows = []
+ self._last_transmissions = []
+ self._drop_arrows = []
+ self._last_drops = []
+ self._show_transmissions_mode = None
+ self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
+ self._panning_state = None
+ self.node_size_adjustment = None
+ self.transmissions_smoothing_adjustment = None
+ self.sample_period = SAMPLE_PERIOD
+ self.node_drag_state = None
+ self.follow_node = None
+ self.shell_window = None
+
+ self.create_gui()
+
+ for plugin in plugins:
+ plugin(self)
+
+ def set_show_transmissions_mode(self, mode):
+ assert isinstance(mode, ShowTransmissionsMode)
+ self._show_transmissions_mode = mode
+ if self._show_transmissions_mode == ShowTransmissionsMode.ALL:
+ self.simulation.set_nodes_of_interest(range(ns3.NodeList.GetNNodes()))
+ elif self._show_transmissions_mode == ShowTransmissionsMode.NONE:
+ self.simulation.set_nodes_of_interest([])
+ elif self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
+ if self.selected_node is None:
+ self.simulation.set_nodes_of_interest([])
+ else:
+ self.simulation.set_nodes_of_interest([self.selected_node.node_index])
+
+ def _create_advanced_controls(self):
+ expander = gtk.Expander("Advanced")
+ expander.show()
+
+ main_vbox = gobject.new(gtk.VBox, border_width=8, visible=True)
+ expander.add(main_vbox)
+
+ main_hbox1 = gobject.new(gtk.HBox, border_width=8, visible=True)
+ main_vbox.pack_start(main_hbox1)
+
+ show_transmissions_group = HIGContainer("Show transmissions")
+ show_transmissions_group.show()
+ main_hbox1.pack_start(show_transmissions_group, False, False, 8)
+
+ vbox = gtk.VBox(True, 4)
+ vbox.show()
+ show_transmissions_group.add(vbox)
+
+ all_nodes = gtk.RadioButton(None)
+ all_nodes.set_label("All nodes")
+ all_nodes.set_active(True)
+ all_nodes.show()
+ vbox.add(all_nodes)
+
+ selected_node = gtk.RadioButton(all_nodes)
+ selected_node.show()
+ selected_node.set_label("Selected node")
+ selected_node.set_active(False)
+ vbox.add(selected_node)
+
+ no_node = gtk.RadioButton(all_nodes)
+ no_node.show()
+ no_node.set_label("Disabled")
+ no_node.set_active(False)
+ vbox.add(no_node)
+
+ def toggled(radio):
+ if radio.get_active():
+ self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
+ all_nodes.connect("toggled", toggled)
+
+ def toggled(radio):
+ if radio.get_active():
+ self.set_show_transmissions_mode(ShowTransmissionsMode.NONE)
+ no_node.connect("toggled", toggled)
+
+ def toggled(radio):
+ if radio.get_active():
+ self.set_show_transmissions_mode(ShowTransmissionsMode.SELECTED)
+ selected_node.connect("toggled", toggled)
+
+
+ # -- misc settings
+ misc_settings_group = HIGContainer("Misc Settings")
+ misc_settings_group.show()
+ main_hbox1.pack_start(misc_settings_group, False, False, 8)
+ settings_hbox = gobject.new(gtk.HBox, border_width=8, visible=True)
+ misc_settings_group.add(settings_hbox)
+
+ # --> node size
+ vbox = gobject.new(gtk.VBox, border_width=0, visible=True)
+ scale = gobject.new(gtk.HScale, visible=True, digits=2)
+ vbox.pack_start(scale, True, True, 0)
+ vbox.pack_start(gobject.new(gtk.Label, label="Node Size", visible=True), True, True, 0)
+ settings_hbox.pack_start(vbox, False, False, 6)
+ self.node_size_adjustment = scale.get_adjustment()
+ def node_size_changed(adj):
+ for node in self.nodes.itervalues():
+ node.set_size(adj.value)
+ self.node_size_adjustment.connect("value-changed", node_size_changed)
+ self.node_size_adjustment.set_all(DEFAULT_NODE_SIZE, 0.01, 20, 0.1)
+
+ # --> transmissions smooth factor
+ vbox = gobject.new(gtk.VBox, border_width=0, visible=True)
+ scale = gobject.new(gtk.HScale, visible=True, digits=1)
+ vbox.pack_start(scale, True, True, 0)
+ vbox.pack_start(gobject.new(gtk.Label, label="Tx. Smooth Factor (s)", visible=True), True, True, 0)
+ settings_hbox.pack_start(vbox, False, False, 6)
+ self.transmissions_smoothing_adjustment = scale.get_adjustment()
+ self.transmissions_smoothing_adjustment.set_all(DEFAULT_TRANSMISSIONS_MEMORY*0.1, 0.1, 10, 0.1)
+
+ return expander
+
+ class _PanningState(object):
+ __slots__ = ['initial_mouse_pos', 'initial_canvas_pos', 'motion_signal']
+
+ def _begin_panning(self, widget, event):
+ self.canvas.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))
+ self._panning_state = self._PanningState()
+ x, y, dummy = widget.window.get_pointer()
+ self._panning_state.initial_mouse_pos = (x, y)
+ x = self._scrolled_window.get_hadjustment().value
+ y = self._scrolled_window.get_vadjustment().value
+ self._panning_state.initial_canvas_pos = (x, y)
+ self._panning_state.motion_signal = self.canvas.connect("motion-notify-event", self._panning_motion)
+
+ def _end_panning(self, event):
+ if self._panning_state is None:
+ return
+ self.canvas.window.set_cursor(None)
+ self.canvas.disconnect(self._panning_state.motion_signal)
+ self._panning_state = None
+
+ def _panning_motion(self, widget, event):
+ assert self._panning_state is not None
+ if event.is_hint:
+ x, y, dummy = widget.window.get_pointer()
+ else:
+ x, y = event.x, event.y
+
+ hadj = self._scrolled_window.get_hadjustment()
+ vadj = self._scrolled_window.get_vadjustment()
+ mx0, my0 = self._panning_state.initial_mouse_pos
+ cx0, cy0 = self._panning_state.initial_canvas_pos
+
+ dx = x - mx0
+ dy = y - my0
+ hadj.value = cx0 - dx
+ vadj.value = cy0 - dy
+ return True
+
+ def _canvas_button_press(self, widget, event):
+ if event.button == 2:
+ self._begin_panning(widget, event)
+ return True
+ return False
+
+ def _canvas_button_release(self, dummy_widget, event):
+ if event.button == 2:
+ self._end_panning(event)
+ return True
+ return False
+
+ def _canvas_scroll_event(self, dummy_widget, event):
+ if event.direction == gtk.gdk.SCROLL_UP:
+ self.zoom.value *= 1.25
+ return True
+ elif event.direction == gtk.gdk.SCROLL_DOWN:
+ self.zoom.value /= 1.25
+ return True
+ return False
+
+ def get_hadjustment(self):
+ return self._scrolled_window.get_hadjustment()
+ def get_vadjustment(self):
+ return self._scrolled_window.get_vadjustment()
+
+ def create_gui(self):
+ self.window = gtk.Window()
+ vbox = gtk.VBox(); vbox.show()
+ self.window.add(vbox)
+
+ # canvas
+ self.canvas = goocanvas.Canvas()
+ self.canvas.connect_after("button-press-event", self._canvas_button_press)
+ self.canvas.connect_after("button-release-event", self._canvas_button_release)
+ self.canvas.connect("scroll-event", self._canvas_scroll_event)
+ self.canvas.props.has_tooltip = True
+ self.canvas.connect("query-tooltip", self._canvas_tooltip_cb)
+ self.canvas.show()
+ sw = gtk.ScrolledWindow(); sw.show()
+ self._scrolled_window = sw
+ sw.add(self.canvas)
+ vbox.pack_start(sw, True, True, 4)
+ self.canvas.set_size_request(600, 450)
+ self.canvas.set_bounds(-10000, -10000, 10000, 10000)
+ self.canvas.scroll_to(0, 0)
+
+
+ self.canvas.get_root_item().add_child(self.links_group)
+ self.links_group.set_property("visibility", goocanvas.ITEM_VISIBLE)
+
+ self.canvas.get_root_item().add_child(self.channels_group)
+ self.channels_group.set_property("visibility", goocanvas.ITEM_VISIBLE)
+ self.channels_group.raise_(self.links_group)
+
+ self.canvas.get_root_item().add_child(self.nodes_group)
+ self.nodes_group.set_property("visibility", goocanvas.ITEM_VISIBLE)
+ self.nodes_group.raise_(self.channels_group)
+
+ self.hud = hud.Axes(self)
+
+ hbox = gtk.HBox(); hbox.show()
+ vbox.pack_start(hbox, False, False, 4)
+
+ # zoom
+ zoom_adj = gtk.Adjustment(1.0, 0.01, 10.0, 0.02, 1.0, 0)
+ self.zoom = zoom_adj
+ def _zoom_changed(adj):
+ self.canvas.set_scale(adj.value)
+ zoom_adj.connect("value-changed", _zoom_changed)
+ zoom = gtk.SpinButton(zoom_adj)
+ zoom.set_digits(3)
+ zoom.show()
+ hbox.pack_start(gobject.new(gtk.Label, label=" Zoom:", visible=True), False, False, 4)
+ hbox.pack_start(zoom, False, False, 4)
+ _zoom_changed(zoom_adj)
+
+ # speed
+ speed_adj = gtk.Adjustment(1.0, 0.01, 10.0, 0.02, 1.0, 0)
+ def _speed_changed(adj):
+ self.speed = adj.value
+ self.sample_period = SAMPLE_PERIOD*adj.value
+ self._start_update_timer()
+ speed_adj.connect("value-changed", _speed_changed)
+ speed = gtk.SpinButton(speed_adj)
+ speed.set_digits(3)
+ speed.show()
+ hbox.pack_start(gobject.new(gtk.Label, label=" Speed:", visible=True), False, False, 4)
+ hbox.pack_start(speed, False, False, 4)
+ _speed_changed(speed_adj)
+
+ # Current time
+ self.time_label = gobject.new(gtk.Label, label=" Speed:", visible=True)
+ self.time_label.set_width_chars(20)
+ hbox.pack_start(self.time_label, False, False, 4)
+
+ # Screenshot button
+ screenshot_button = gobject.new(gtk.Button,
+ label="Snapshot",
+ relief=gtk.RELIEF_NONE, focus_on_click=False,
+ visible=True)
+ hbox.pack_start(screenshot_button, False, False, 4)
+
+ def load_button_icon(button, icon_name):
+ try:
+ import gnomedesktop
+ except ImportError:
+ sys.stderr.write("Could not load icon %s due to missing gnomedesktop Python module\n" % icon_name)
+ else:
+ icon = gnomedesktop.find_icon(gtk.icon_theme_get_default(), icon_name, 16, 0)
+ if icon is not None:
+ button.props.image = gobject.new(gtk.Image, file=icon, visible=True)
+
+ load_button_icon(screenshot_button, "applets-screenshooter")
+ screenshot_button.connect("clicked", self._take_screenshot)
+
+ # Shell button
+ if ipython_view is not None:
+ shell_button = gobject.new(gtk.Button,
+ label="Shell",
+ relief=gtk.RELIEF_NONE, focus_on_click=False,
+ visible=True)
+ hbox.pack_start(shell_button, False, False, 4)
+ load_button_icon(shell_button, "gnome-terminal")
+ shell_button.connect("clicked", self._start_shell)
+
+ # Play button
+ self.play_button = gobject.new(gtk.ToggleButton,
+ image=gobject.new(gtk.Image, stock=gtk.STOCK_MEDIA_PLAY, visible=True),
+ label="Simulate (F3)",
+ relief=gtk.RELIEF_NONE, focus_on_click=False,
+ use_stock=True, visible=True)
+ accel_group = gtk.AccelGroup()
+ self.window.add_accel_group(accel_group)
+ self.play_button.add_accelerator("clicked", accel_group,
+ gtk.keysyms.F3, 0, gtk.ACCEL_VISIBLE)
+ self.play_button.connect("toggled", self._on_play_button_toggled)
+ hbox.pack_start(self.play_button, False, False, 4)
+
+ self.canvas.get_root_item().connect("button-press-event", self.on_root_button_press_event)
+
+ vbox.pack_start(self._create_advanced_controls(), False, False, 4)
+
+ self.window.show()
+
+ def scan_topology(self):
+ print "scanning topology: %i nodes..." % (ns3.NodeList.GetNNodes(),)
+ graph = pygraphviz.AGraph()
+ seen_nodes = 0
+ for nodeI in range(ns3.NodeList.GetNNodes()):
+ seen_nodes += 1
+ if seen_nodes == 100:
+ print "scan topology... %i nodes visited (%.1f%%)" % (nodeI, 100*nodeI/ns3.NodeList.GetNNodes())
+ seen_nodes = 0
+ node = ns3.NodeList.GetNode(nodeI)
+ node_name = "Node %i" % nodeI
+ node_view = self.get_node(nodeI)
+
+ mobility = node.GetObject(ns3.MobilityModel.GetTypeId())
+ if mobility is not None:
+ node_view.set_color("red")
+ pos = mobility.GetPosition()
+ node_view.set_position(*transform_point_simulation_to_canvas(pos.x, pos.y))
+ #print "node has mobility position -> ", "%f,%f" % (pos.x, pos.y)
+ else:
+ graph.add_node(node_name)
+
+ for devI in range(node.GetNDevices()):
+ device = node.GetDevice(devI)
+ device_traits = lookup_netdevice_traits(type(device))
+ if device_traits.is_wireless:
+ continue
+ if device_traits.is_virtual:
+ continue
+ channel = device.GetChannel()
+ if channel.GetNDevices() > 2:
+ if REPRESENT_CHANNELS_AS_NODES:
+ # represent channels as white nodes
+ if mobility is None:
+ channel_name = "Channel %s" % id(channel)
+ graph.add_edge(node_name, channel_name)
+ self.get_channel(channel)
+ self.create_link(self.get_node(nodeI), self.get_channel(channel))
+ else:
+ # don't represent channels, just add links between nodes in the same channel
+ for otherDevI in range(channel.GetNDevices()):
+ otherDev = channel.GetDevice(otherDevI)
+ otherNode = otherDev.GetNode()
+ otherNodeView = self.get_node(otherNode.GetId())
+ if otherNode is not node:
+ if mobility is None and not otherNodeView.has_mobility:
+ other_node_name = "Node %i" % otherNode.GetId()
+ graph.add_edge(node_name, other_node_name)
+ self.create_link(self.get_node(nodeI), otherNodeView)
+ else:
+ for otherDevI in range(channel.GetNDevices()):
+ otherDev = channel.GetDevice(otherDevI)
+ otherNode = otherDev.GetNode()
+ otherNodeView = self.get_node(otherNode.GetId())
+ if otherNode is not node:
+ if mobility is None and not otherNodeView.has_mobility:
+ other_node_name = "Node %i" % otherNode.GetId()
+ graph.add_edge(node_name, other_node_name)
+ self.create_link(self.get_node(nodeI), otherNodeView)
+
+ print "scanning topology: calling graphviz layout"
+ graph.layout(LAYOUT_ALGORITHM)
+ for node in graph.iternodes():
+ #print node, "=>", node.attr['pos']
+ node_type, node_id = node.split(' ')
+ pos_x, pos_y = [float(s) for s in node.attr['pos'].split(',')]
+ if node_type == 'Node':
+ obj = self.nodes[int(node_id)]
+ elif node_type == 'Channel':
+ obj = self.channels[int(node_id)]
+ obj.set_position(pos_x, pos_y)
+
+ print "scanning topology: all done."
+ self.emit("topology-scanned")
+
+ def get_node(self, index):
+ try:
+ return self.nodes[index]
+ except KeyError:
+ node = Node(self, index)
+ self.nodes[index] = node
+ self.nodes_group.add_child(node.canvas_item)
+ node.canvas_item.connect("button-press-event", self.on_node_button_press_event, node)
+ node.canvas_item.connect("button-release-event", self.on_node_button_release_event, node)
+ return node
+
+ def get_channel(self, ns3_channel):
+ try:
+ return self.channels[id(ns3_channel)]
+ except KeyError:
+ channel = Channel(ns3_channel)
+ self.channels[id(ns3_channel)] = channel
+ self.channels_group.add_child(channel.canvas_item)
+ return channel
+
+ def create_link(self, node, node_or_channel):
+ link = WiredLink(node, node_or_channel)
+ self.links_group.add_child(link.canvas_item)
+ link.canvas_item.lower(None)
+
+ def update_view(self):
+ #print "update_view"
+
+ self.time_label.set_text("Time: %f s" % ns3.Simulator.Now().GetSeconds())
+
+ self._update_node_positions()
+
+ # Update information
+ for info_win in self.information_windows:
+ info_win.update()
+
+ self._update_transmissions_view()
+ self._update_drops_view()
+
+ self.emit("update-view")
+
+ def _update_node_positions(self):
+ for node in self.nodes.itervalues():
+ if node.has_mobility:
+ ns3_node = ns3.NodeList.GetNode(node.node_index)
+ mobility = ns3_node.GetObject(ns3.MobilityModel.GetTypeId())
+ if mobility is not None:
+ pos = mobility.GetPosition()
+ x, y = transform_point_simulation_to_canvas(pos.x, pos.y)
+ node.set_position(x, y)
+ if node is self.follow_node:
+ hadj = self._scrolled_window.get_hadjustment()
+ vadj = self._scrolled_window.get_vadjustment()
+ px, py = self.canvas.convert_to_pixels(x, y)
+ hadj.value = px - hadj.page_size/2
+ vadj.value = py - vadj.page_size/2
+
+ def center_on_node(self, node):
+ if isinstance(node, ns3.Node):
+ node = self.nodes[node.GetId()]
+ elif isinstance(node, (int, long)):
+ node = self.nodes[node]
+ elif isinstance(node, Node):
+ pass
+ else:
+ raise TypeError("expected int, viz.Node or ns3.Node, not %r" % node)
+
+ x, y = node.get_position()
+ hadj = self._scrolled_window.get_hadjustment()
+ vadj = self._scrolled_window.get_vadjustment()
+ px, py = self.canvas.convert_to_pixels(x, y)
+ hadj.value = px - hadj.page_size/2
+ vadj.value = py - vadj.page_size/2
+
+
+ def update_model(self):
+ self.simulation.lock.acquire()
+ try:
+ self.emit("simulation-periodic-update")
+ finally:
+ self.simulation.lock.release()
+
+ def do_simulation_periodic_update(self):
+ smooth_factor = int(self.transmissions_smoothing_adjustment.value*10)
+
+ transmissions = self.simulation.sim_helper.GetTransmissionSamples()
+ self._last_transmissions.append(transmissions)
+ while len(self._last_transmissions) > smooth_factor:
+ self._last_transmissions.pop(0)
+
+ drops = self.simulation.sim_helper.GetPacketDropSamples()
+ self._last_drops.append(drops)
+ while len(self._last_drops) > smooth_factor:
+ self._last_drops.pop(0)
+
+ def _get_label_over_line_position(self, pos1_x, pos1_y, pos2_x, pos2_y):
+ hadj = self._scrolled_window.get_hadjustment()
+ vadj = self._scrolled_window.get_vadjustment()
+ bounds_x1, bounds_y1 = self.canvas.convert_from_pixels(hadj.value, vadj.value)
+ bounds_x2, bounds_y2 = self.canvas.convert_from_pixels(hadj.value + hadj.page_size,
+ vadj.value + vadj.page_size)
+ pos1_x, pos1_y, pos2_x, pos2_y = ns3.PyViz.LineClipping(bounds_x1, bounds_y1,
+ bounds_x2, bounds_y2,
+ pos1_x, pos1_y,
+ pos2_x, pos2_y)
+ return (pos1_x + pos2_x)/2, (pos1_y + pos2_y)/2
+
+ def _update_transmissions_view(self):
+ transmissions_average = {}
+ for transmission_set in self._last_transmissions:
+ for transmission in transmission_set:
+ key = (transmission.transmitter.GetId(), transmission.receiver.GetId())
+ rx_bytes, count = transmissions_average.get(key, (0, 0))
+ rx_bytes += transmission.bytes
+ count += 1
+ transmissions_average[key] = rx_bytes, count
+
+ old_arrows = self._transmission_arrows
+ for arrow, label in old_arrows:
+ arrow.set_property("visibility", goocanvas.ITEM_HIDDEN)
+ label.set_property("visibility", goocanvas.ITEM_HIDDEN)
+ new_arrows = []
+
+ k = self.node_size_adjustment.value/5
+
+ for (transmitter_id, receiver_id), (rx_bytes, rx_count) in transmissions_average.iteritems():
+ transmitter = self.get_node(transmitter_id)
+ receiver = self.get_node(receiver_id)
+ try:
+ arrow, label = old_arrows.pop()
+ except IndexError:
+ arrow = goocanvas.Polyline(line_width=2.0, stroke_color_rgba=0x00C000C0, close_path=False, end_arrow=True)
+ arrow.set_property("parent", self.canvas.get_root_item())
+ arrow.props.pointer_events = 0
+ arrow.raise_(None)
+
+ label = goocanvas.Text(parent=self.canvas.get_root_item(), pointer_events=0)
+ label.raise_(None)
+
+ arrow.set_property("visibility", goocanvas.ITEM_VISIBLE)
+ line_width = max(0.1, math.log(float(rx_bytes)/rx_count/self.sample_period)*k)
+ arrow.set_property("line-width", line_width)
+
+ pos1_x, pos1_y = transmitter.get_position()
+ pos2_x, pos2_y = receiver.get_position()
+ points = goocanvas.Points([(pos1_x, pos1_y), (pos2_x, pos2_y)])
+ arrow.set_property("points", points)
+
+ kbps = float(rx_bytes*8)/1e3/rx_count/self.sample_period
+ label.set_properties(visibility=goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD,
+ visibility_threshold=0.5,
+ font=("Sans Serif %f" % int(1+BITRATE_FONT_SIZE*k)))
+ angle = math.atan2((pos2_y - pos1_y), (pos2_x - pos1_x))
+ if -PI_OVER_2 <= angle <= PI_OVER_2:
+ label.set_properties(text=("%.2f kbit/s →" % (kbps,)),
+ alignment=pango.ALIGN_CENTER,
+ anchor=gtk.ANCHOR_S,
+ x=0, y=-line_width/2)
+ M = cairo.Matrix()
+ M.translate(*self._get_label_over_line_position(pos1_x, pos1_y, pos2_x, pos2_y))
+ M.rotate(angle)
+ label.set_transform(M)
+ else:
+ label.set_properties(text=("← %.2f kbit/s" % (kbps,)),
+ alignment=pango.ALIGN_CENTER,
+ anchor=gtk.ANCHOR_N,
+ x=0, y=line_width/2)
+ M = cairo.Matrix()
+ M.translate(*self._get_label_over_line_position(pos1_x, pos1_y, pos2_x, pos2_y))
+ M.rotate(angle)
+ M.scale(-1, -1)
+ label.set_transform(M)
+
+ new_arrows.append((arrow, label))
+
+ self._transmission_arrows = new_arrows + old_arrows
+
+
+ def _update_drops_view(self):
+ drops_average = {}
+ for drop_set in self._last_drops:
+ for drop in drop_set:
+ key = drop.transmitter.GetId()
+ drop_bytes, count = drops_average.get(key, (0, 0))
+ drop_bytes += drop.bytes
+ count += 1
+ drops_average[key] = drop_bytes, count
+
+ old_arrows = self._drop_arrows
+ for arrow, label in old_arrows:
+ arrow.set_property("visibility", goocanvas.ITEM_HIDDEN)
+ label.set_property("visibility", goocanvas.ITEM_HIDDEN)
+ new_arrows = []
+
+ # get the coordinates for the edge of screen
+ vadjustment = self._scrolled_window.get_vadjustment()
+ bottom_y = vadjustment.value + vadjustment.page_size
+ dummy, edge_y = self.canvas.convert_from_pixels(0, bottom_y)
+
+ k = self.node_size_adjustment.value/5
+
+ for transmitter_id, (drop_bytes, drop_count) in drops_average.iteritems():
+ transmitter = self.get_node(transmitter_id)
+ try:
+ arrow, label = old_arrows.pop()
+ except IndexError:
+ arrow = goocanvas.Polyline(line_width=2.0, stroke_color_rgba=0xC00000C0, close_path=False, end_arrow=True)
+ arrow.props.pointer_events = 0
+ arrow.set_property("parent", self.canvas.get_root_item())
+ arrow.raise_(None)
+
+ label = goocanvas.Text()#, fill_color_rgba=0x00C000C0)
+ label.props.pointer_events = 0
+ label.set_property("parent", self.canvas.get_root_item())
+ label.raise_(None)
+
+ arrow.set_property("visibility", goocanvas.ITEM_VISIBLE)
+ arrow.set_property("line-width", max(0.1, math.log(float(drop_bytes)/drop_count/self.sample_period)*k))
+ pos1_x, pos1_y = transmitter.get_position()
+ pos2_x, pos2_y = pos1_x, edge_y
+ points = goocanvas.Points([(pos1_x, pos1_y), (pos2_x, pos2_y)])
+ arrow.set_property("points", points)
+
+ label.set_properties(visibility=goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD,
+ visibility_threshold=0.5,
+ font=("Sans Serif %i" % int(1+BITRATE_FONT_SIZE*k)),
+ text=("%.2f kbit/s" % (float(drop_bytes*8)/1e3/drop_count/self.sample_period,)),
+ alignment=pango.ALIGN_CENTER,
+ x=(pos1_x + pos2_x)/2,
+ y=(pos1_y + pos2_y)/2)
+
+ new_arrows.append((arrow, label))
+
+ self._drop_arrows = new_arrows + old_arrows
+
+
+ def update_view_timeout(self):
+ #print "view: update_view_timeout called at real time ", time.time()
+
+ # while the simulator is busy, run the gtk event loop
+ while not self.simulation.lock.acquire(False):
+ while gtk.events_pending():
+ gtk.main_iteration()
+ pause_messages = self.simulation.pause_messages
+ self.simulation.pause_messages = []
+ try:
+ self.update_view()
+ self.simulation.target_time = ns3.Simulator.Now ().GetSeconds () + self.sample_period
+ #print "view: target time set to %f" % self.simulation.target_time
+ finally:
+ self.simulation.lock.release()
+
+ if pause_messages:
+ #print pause_messages
+ dialog = gtk.MessageDialog(parent=self.window, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_OK,
+ message_format='\n'.join(pause_messages))
+ dialog.connect("response", lambda d, r: d.destroy())
+ dialog.show()
+ self.play_button.set_active(False)
+
+ # if we're paused, stop the update timer
+ if not self.play_button.get_active():
+ self._update_timeout_id = None
+ return False
+
+ #print "view: self.simulation.go.set()"
+ self.simulation.go.set()
+ #print "view: done."
+ return True
+
+ def _start_update_timer(self):
+ if self._update_timeout_id is not None:
+ gobject.source_remove(self._update_timeout_id)
+ #print "start_update_timer"
+ self._update_timeout_id = gobject.timeout_add(int(SAMPLE_PERIOD/min(self.speed, 1)*1e3),
+ self.update_view_timeout,
+ priority=PRIORITY_UPDATE_VIEW)
+
+ def _on_play_button_toggled(self, button):
+ if button.get_active():
+ self._start_update_timer()
+ else:
+ if self._update_timeout_id is not None:
+ gobject.source_remove(self._update_timeout_id)
+
+ def _quit(self, *dummy_args):
+ if self._update_timeout_id is not None:
+ gobject.source_remove(self._update_timeout_id)
+ self._update_timeout_id = None
+ self.simulation.quit = True
+ self.simulation.go.set()
+ self.simulation.join()
+ gtk.main_quit()
+
+ def _monkey_patch_ipython(self):
+ # The user may want to access the NS 3 simulation state, but
+ # NS 3 is not thread safe, so it could cause serious problems.
+ # To work around this, monkey-patch IPython to automatically
+ # acquire and release the simulation lock around each code
+ # that is executed.
+
+ original_runcode = __IPYTHON__.runcode
+ def runcode(ip, *args):
+ #print "lock"
+ self.simulation.lock.acquire()
+ try:
+ return original_runcode(*args)
+ finally:
+ #print "unlock"
+ self.simulation.lock.release()
+ import types
+ __IPYTHON__.runcode = types.MethodType(runcode, __IPYTHON__)
+
+ def autoscale_view(self):
+ self._update_node_positions()
+ positions = [node.get_position() for node in self.nodes.itervalues()]
+ min_x, min_y = min(x for (x,y) in positions), min(y for (x,y) in positions)
+ max_x, max_y = max(x for (x,y) in positions), max(y for (x,y) in positions)
+ min_x_px, min_y_px = self.canvas.convert_to_pixels(min_x, min_y)
+ max_x_px, max_y_px = self.canvas.convert_to_pixels(max_x, max_y)
+ dx = max_x - min_x
+ dy = max_y - min_y
+ dx_px = max_x_px - min_x_px
+ dy_px = max_y_px - min_y_px
+ hadj = self._scrolled_window.get_hadjustment()
+ vadj = self._scrolled_window.get_vadjustment()
+ new_dx, new_dy = 1.5*dx_px, 1.5*dy_px
+
+ if new_dx == 0 or new_dy == 0:
+ return
+
+ self.zoom.value = min(hadj.page_size/new_dx, vadj.page_size/new_dy)
+
+ x1, y1 = self.canvas.convert_from_pixels(hadj.value, vadj.value)
+ x2, y2 = self.canvas.convert_from_pixels(hadj.value+hadj.page_size, vadj.value+vadj.page_size)
+ width = x2 - x1
+ height = y2 - y1
+ center_x = (min_x + max_x) / 2
+ center_y = (min_y + max_y) / 2
+
+ self.canvas.scroll_to(center_x - width/2, center_y - height/2)
+
+ return False
+
+ def start(self):
+ self.scan_topology()
+ self.window.connect("delete-event", self._quit)
+ #self._start_update_timer()
+ gobject.timeout_add(200, self.autoscale_view)
+ self.simulation.start()
+
+ try:
+ __IPYTHON__
+ except NameError:
+ pass
+ else:
+ self._monkey_patch_ipython()
+
+ gtk.main()
+
+
+ def on_root_button_press_event(self, view, target, event):
+ if event.button == 1:
+ self.select_node(None)
+ return True
+
+ def on_node_button_press_event(self, view, target, event, node):
+ if event.button == 1:
+ self.select_node(node)
+ return True
+ elif event.button == 3:
+ self.popup_node_menu(node, event)
+ return True
+ elif event.button == 2:
+ self.begin_node_drag(node)
+ return True
+ return False
+
+ def on_node_button_release_event(self, view, target, event, node):
+ if event.button == 2:
+ self.end_node_drag(node)
+ return True
+ return False
+
+ class NodeDragState(object):
+ def __init__(self, canvas_x0, canvas_y0, sim_x0, sim_y0):
+ self.canvas_x0 = canvas_x0
+ self.canvas_y0 = canvas_y0
+ self.sim_x0 = sim_x0
+ self.sim_y0 = sim_y0
+ self.motion_signal = None
+
+ def begin_node_drag(self, node):
+ self.simulation.lock.acquire()
+ try:
+ ns3_node = ns3.NodeList.GetNode(node.node_index)
+ mob = ns3_node.GetObject(ns3.MobilityModel.GetTypeId())
+ if mob is None:
+ return
+ if self.node_drag_state is not None:
+ return
+ pos = mob.GetPosition()
+ finally:
+ self.simulation.lock.release()
+ x, y, dummy = self.canvas.window.get_pointer()
+ x0, y0 = self.canvas.convert_from_pixels(x, y)
+ self.node_drag_state = self.NodeDragState(x0, y0, pos.x, pos.y)
+ self.node_drag_state.motion_signal = node.canvas_item.connect("motion-notify-event", self.node_drag_motion, node)
+
+ def node_drag_motion(self, item, targe_item, event, node):
+ self.simulation.lock.acquire()
+ try:
+ ns3_node = ns3.NodeList.GetNode(node.node_index)
+ mob = ns3_node.GetObject(ns3.MobilityModel.GetTypeId())
+ if mob is None:
+ return False
+ if self.node_drag_state is None:
+ return False
+ x, y, dummy = self.canvas.window.get_pointer()
+ canvas_x, canvas_y = self.canvas.convert_from_pixels(x, y)
+ dx = (canvas_x - self.node_drag_state.canvas_x0)
+ dy = (canvas_y - self.node_drag_state.canvas_y0)
+ pos = mob.GetPosition()
+ pos.x = self.node_drag_state.sim_x0 + transform_distance_canvas_to_simulation(dx)
+ pos.y = self.node_drag_state.sim_y0 + transform_distance_canvas_to_simulation(dy)
+ #print "SetPosition(%G, %G)" % (pos.x, pos.y)
+ mob.SetPosition(pos)
+ node.set_position(*transform_point_simulation_to_canvas(pos.x, pos.y))
+ finally:
+ self.simulation.lock.release()
+ return True
+
+ def end_node_drag(self, node):
+ if self.node_drag_state is None:
+ return
+ node.canvas_item.disconnect(self.node_drag_state.motion_signal)
+ self.node_drag_state = None
+
+ def popup_node_menu(self, node, event):
+ menu = gtk.Menu()
+ self.emit("populate-node-menu", node, menu)
+ menu.popup(None, None, None, event.button, event.time)
+
+ def _update_ipython_selected_node(self):
+ # If we are running under ipython -gthread, make this new
+ # selected node available as a global 'selected_node'
+ # variable.
+ try:
+ __IPYTHON__
+ except NameError:
+ pass
+ else:
+ if self.selected_node is None:
+ ns3_node = None
+ else:
+ self.simulation.lock.acquire()
+ try:
+ ns3_node = ns3.NodeList.GetNode(self.selected_node.node_index)
+ finally:
+ self.simulation.lock.release()
+ __IPYTHON__.user_ns['selected_node'] = ns3_node
+
+
+ def select_node(self, node):
+ if isinstance(node, ns3.Node):
+ node = self.nodes[node.GetId()]
+ elif isinstance(node, (int, long)):
+ node = self.nodes[node]
+ elif isinstance(node, Node):
+ pass
+ elif node is None:
+ pass
+ else:
+ raise TypeError("expected None, int, viz.Node or ns3.Node, not %r" % node)
+
+ if node is self.selected_node:
+ return
+
+ if self.selected_node is not None:
+ self.selected_node.selected = False
+ self.selected_node = node
+ if self.selected_node is not None:
+ self.selected_node.selected = True
+
+ if self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
+ if self.selected_node is None:
+ self.simulation.set_nodes_of_interest([])
+ else:
+ self.simulation.set_nodes_of_interest([self.selected_node.node_index])
+
+ self._update_ipython_selected_node()
+
+
+ def add_information_window(self, info_win):
+ self.information_windows.append(info_win)
+ self.simulation.lock.acquire()
+ try:
+ info_win.update()
+ finally:
+ self.simulation.lock.release()
+
+ def remove_information_window(self, info_win):
+ self.information_windows.remove(info_win)
+
+ def _canvas_tooltip_cb(self, canvas, x, y, keyboard_mode, tooltip):
+ #print "tooltip query: ", x, y
+ hadj = self._scrolled_window.get_hadjustment()
+ vadj = self._scrolled_window.get_vadjustment()
+ x, y = self.canvas.convert_from_pixels(hadj.value + x, vadj.value + y)
+ item = self.canvas.get_item_at(x, y, True)
+ #print "items at (%f, %f): %r | keyboard_mode=%r" % (x, y, item, keyboard_mode)
+ if not item:
+ return False
+ while item is not None:
+ obj = item.get_data("pyviz-object")
+ if obj is not None:
+ obj.tooltip_query(tooltip)
+ return True
+ item = item.props.parent
+ return False
+
+ def _get_export_file_name(self):
+ sel = gtk.FileChooserDialog("Save...", self.canvas.get_toplevel(),
+ gtk.FILE_CHOOSER_ACTION_SAVE,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_SAVE, gtk.RESPONSE_OK))
+ sel.set_default_response(gtk.RESPONSE_OK)
+ sel.set_local_only(True)
+ sel.set_do_overwrite_confirmation(True)
+ sel.set_current_name("Unnamed.pdf")
+
+ filter = gtk.FileFilter()
+ filter.set_name("Embedded PostScript")
+ filter.add_mime_type("image/x-eps")
+ sel.add_filter(filter)
+
+ filter = gtk.FileFilter()
+ filter.set_name("Portable Document Graphics")
+ filter.add_mime_type("application/pdf")
+ sel.add_filter(filter)
+
+ filter = gtk.FileFilter()
+ filter.set_name("Scalable Vector Graphics")
+ filter.add_mime_type("image/svg+xml")
+ sel.add_filter(filter)
+
+ resp = sel.run()
+ if resp != gtk.RESPONSE_OK:
+ sel.destroy()
+ return None
+
+ file_name = sel.get_filename()
+ sel.destroy()
+ return file_name
+
+ def _take_screenshot(self, dummy_button):
+ #print "Cheese!"
+ file_name = self._get_export_file_name()
+ if file_name is None:
+ return
+
+ # figure out the correct bounding box for what is visible on screen
+ x1 = self._scrolled_window.get_hadjustment().value
+ y1 = self._scrolled_window.get_vadjustment().value
+ x2 = x1 + self._scrolled_window.get_hadjustment().page_size
+ y2 = y1 + self._scrolled_window.get_vadjustment().page_size
+ bounds = goocanvas.Bounds()
+ bounds.x1, bounds.y1 = self.canvas.convert_from_pixels(x1, y1)
+ bounds.x2, bounds.y2 = self.canvas.convert_from_pixels(x2, y2)
+ dest_width = bounds.x2 - bounds.x1
+ dest_height = bounds.y2 - bounds.y1
+ #print bounds.x1, bounds.y1, " -> ", bounds.x2, bounds.y2
+
+ dummy, extension = os.path.splitext(file_name)
+ extension = extension.lower()
+ if extension == '.eps':
+ surface = cairo.PSSurface(file_name, dest_width, dest_height)
+ elif extension == '.pdf':
+ surface = cairo.PDFSurface(file_name, dest_width, dest_height)
+ elif extension == '.svg':
+ surface = cairo.SVGSurface(file_name, dest_width, dest_height)
+ else:
+ dialog = gtk.MessageDialog(parent = self.canvas.get_toplevel(),
+ flags = gtk.DIALOG_DESTROY_WITH_PARENT,
+ type = gtk.MESSAGE_ERROR,
+ buttons = gtk.BUTTONS_OK,
+ message_format = "Unknown extension '%s' (valid extensions are '.eps', '.svg', and '.pdf')"
+ % (extension,))
+ dialog.run()
+ dialog.destroy()
+ return
+
+ # draw the canvas to a printing context
+ cr = cairo.Context(surface)
+ cr.translate(-bounds.x1, -bounds.y1)
+ self.canvas.render(cr, bounds, self.zoom.value)
+ cr.show_page()
+ surface.finish()
+
+ def set_follow_node(self, node):
+ if isinstance(node, ns3.Node):
+ node = self.nodes[node.GetId()]
+ self.follow_node = node
+
+ def _start_shell(self, dummy_button):
+ if self.shell_window is not None:
+ self.shell_window.present()
+ return
+
+ self.shell_window = gtk.Window()
+ self.shell_window.set_size_request(750,550)
+ self.shell_window.set_resizable(True)
+ scrolled_window = gtk.ScrolledWindow()
+ scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
+ ipython = ipython_view.IPythonView()
+ ipython.modify_font(pango.FontDescription(SHELL_FONT))
+ ipython.set_wrap_mode(gtk.WRAP_CHAR)
+ ipython.show()
+ scrolled_window.add(ipython)
+ scrolled_window.show()
+ self.shell_window.add(scrolled_window)
+ self.shell_window.show()
+ self.shell_window.connect('destroy', self._on_shell_window_destroy)
+
+ self._update_ipython_selected_node()
+ __IPYTHON__.user_ns['viz'] = self
+
+
+ def _on_shell_window_destroy(self, window):
+ self.shell_window = None
+
+
+initialization_hooks = []
+
+def add_initialization_hook(hook, *args):
+ """
+ Adds a callback to be called after
+ the visualizer is initialized, like this::
+ initialization_hook(visualizer, *args)
+ """
+ global initialization_hooks
+ initialization_hooks.append((hook, args))
+
+
+def set_bounds(x1, y1, x2, y2):
+ assert x2>x1
+ assert y2>y1
+ def hook(viz):
+ cx1, cy1 = transform_point_simulation_to_canvas(x1, y1)
+ cx2, cy2 = transform_point_simulation_to_canvas(x2, y2)
+ viz.canvas.set_bounds(cx1, cy1, cx2, cy2)
+ add_initialization_hook(hook)
+
+
+def start():
+ assert Visualizer.INSTANCE is None
+ if _import_error is not None:
+ import sys
+ print >> sys.stderr, "No visualization support (%s)." % (str(_import_error),)
+ ns3.Simulator.Run()
+ return
+ load_plugins()
+ viz = Visualizer()
+ for hook, args in initialization_hooks:
+ gobject.idle_add(hook, viz, *args)
+ ns3.Packet.EnablePrinting()
+ viz.start()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/higcontainer.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,97 @@
+import gtk
+import gobject
+try:
+ from gazpacho.widgets.base.base import SimpleContainerAdaptor
+except ImportError:
+ pass
+
+#root_library = 'hig'
+
+
+class HIGContainer(gtk.Bin):
+ __gtype_name__ = 'HIGContainer'
+ __gproperties__ = {
+ 'title': (str, 'Group Title', 'the group title',
+ '', gobject.PARAM_READWRITE|gobject.PARAM_CONSTRUCT),
+ }
+
+ def __init__(self, title=None):
+ self.__title_text = None
+ gtk.widget_push_composite_child()
+ self.__title = gobject.new(gtk.Label, visible=True, xalign=0, yalign=0.5)
+ self.__indent = gobject.new(gtk.Label, visible=True, label=' ')
+ gtk.widget_pop_composite_child()
+ gtk.Bin.__init__(self)
+ self.__title.set_parent(self)
+ self.__indent.set_parent(self)
+ if title is not None:
+ self.props.title = title
+
+ def do_size_request(self, requisition):
+ title_req = gtk.gdk.Rectangle(0, 0, *self.__title.size_request())
+ indent_req = gtk.gdk.Rectangle(0, 0, *self.__indent.size_request())
+ if self.child is None:
+ child_req = gtk.gdk.Rectangle()
+ else:
+ child_req = gtk.gdk.Rectangle(0, 0, *self.child.size_request())
+ requisition.height = (title_req.height + 6 +
+ max(child_req.height, indent_req.height))
+ requisition.width = max(title_req.width, indent_req.width + child_req.width)
+
+ def do_size_allocate(self, allocation):
+ self.allocation = allocation
+
+ ## title
+ title_req = gtk.gdk.Rectangle(0, 0, *self.__title.get_child_requisition())
+ title_alloc = gtk.gdk.Rectangle()
+ title_alloc.x = allocation.x
+ title_alloc.y = allocation.y
+ title_alloc.width = min(title_req.width, allocation.width)
+ title_alloc.height = min(title_req.height, allocation.height)
+ self.__title.size_allocate(title_alloc)
+
+ ## child
+ if self.child is None:
+ return
+ indent_req = gtk.gdk.Rectangle(0, 0, *self.__indent.get_child_requisition())
+ child_req = gtk.gdk.Rectangle(0, 0, *self.child.get_child_requisition())
+ child_alloc = gtk.gdk.Rectangle()
+ child_alloc.x = allocation.x + indent_req.width
+ child_alloc.y = allocation.y + title_alloc.height + 6
+ child_alloc.width = allocation.width - indent_req.width
+ child_alloc.height = allocation.height - 6 - title_alloc.height
+ self.child.size_allocate(child_alloc)
+
+ def do_forall(self, internal, callback, data):
+ if internal:
+ callback(self.__title, data)
+ callback(self.__indent, data)
+ if self.child is not None:
+ callback(self.child, data)
+
+ def do_set_property(self, pspec, value):
+ if pspec.name == 'title':
+ self.__title.set_markup('<span weight="bold">%s</span>' %
+ gobject.markup_escape_text(value))
+ self.__title_text = value
+ else:
+ raise AttributeError, 'unknown property %s' % pspec.name
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'title':
+ return self.__title_text
+ else:
+ raise AttributeError, 'unknown property %s' % pspec.name
+
+if __name__ == '__main__':
+ frame = gtk.Frame()
+ group = gobject.new(HIGContainer, title="Hello")
+ frame.add(group)
+ check = gtk.CheckButton("foobar")
+ group.add(check)
+ w = gtk.Window()
+ w.add(frame)
+ w.show_all()
+ w.connect("destroy", lambda w: gtk.main_quit())
+ gtk.main()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/hud.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,144 @@
+import goocanvas
+import core
+import math
+import pango
+import gtk
+
+
+class Axes(object):
+ def __init__(self, viz):
+ self.viz = viz
+ self.color = 0x8080C0FF
+ self.hlines = goocanvas.Path(parent=viz.canvas.get_root_item(), stroke_color_rgba=self.color)
+ self.hlines.lower(None)
+ self.vlines = goocanvas.Path(parent=viz.canvas.get_root_item(), stroke_color_rgba=self.color)
+ self.vlines.lower(None)
+ self.labels = []
+ hadj = self.viz.get_hadjustment()
+ vadj = self.viz.get_vadjustment()
+ def update(adj):
+ if self.visible:
+ self.update_view()
+ hadj.connect("value-changed", update)
+ vadj.connect("value-changed", update)
+ hadj.connect("changed", update)
+ vadj.connect("changed", update)
+ self.visible = True
+ self.update_view()
+
+ def set_visible(self, visible):
+ self.visible = visible
+ if self.visible:
+ self.hlines.props.visibility = goocanvas.ITEM_VISIBLE
+ self.vlines.props.visibility = goocanvas.ITEM_VISIBLE
+ else:
+ self.hlines.props.visibility = goocanvas.ITEM_HIDDEN
+ self.vlines.props.visibility = goocanvas.ITEM_HIDDEN
+ for label in self.labels:
+ label.props.visibility = goocanvas.ITEM_HIDDEN
+
+ def _compute_divisions(self, xi, xf):
+ assert xf > xi
+ dx = xf - xi
+ size = dx
+ ndiv = 5
+ text_width = dx/ndiv/2
+
+ def rint(x):
+ return math.floor(x+0.5)
+
+ dx_over_ndiv = dx / ndiv
+ for n in range(5): # iterate 5 times to find optimum division size
+ #/* div: length of each division */
+ tbe = math.log10(dx_over_ndiv)#; /* looking for approx. 'ndiv' divisions in a length 'dx' */
+ div = pow(10, rint(tbe))#; /* div: power of 10 closest to dx/ndiv */
+ if math.fabs(div/2 - dx_over_ndiv) < math.fabs(div - dx_over_ndiv): #/* test if div/2 is closer to dx/ndiv */
+ div /= 2
+ elif math.fabs(div*2 - dx_over_ndiv) < math.fabs(div - dx_over_ndiv):
+ div *= 2 # /* test if div*2 is closer to dx/ndiv */
+ x0 = div*math.ceil(xi / div) - div
+ if n > 1:
+ ndiv = rint(size / text_width)
+ return x0, div
+
+
+ def update_view(self):
+ if self.viz.zoom is None:
+ return
+
+ unused_labels = self.labels
+ self.labels = []
+ for label in unused_labels:
+ label.set_property("visibility", goocanvas.ITEM_HIDDEN)
+ def get_label():
+ try:
+ label = unused_labels.pop(0)
+ except IndexError:
+ label = goocanvas.Text(parent=self.viz.canvas.get_root_item(), stroke_color_rgba=self.color)
+ else:
+ label.set_property("visibility", goocanvas.ITEM_VISIBLE)
+ label.lower(None)
+ self.labels.append(label)
+ return label
+
+ hadj = self.viz.get_hadjustment()
+ vadj = self.viz.get_vadjustment()
+ zoom = self.viz.zoom.value
+ offset = 10/zoom
+
+ x1, y1 = self.viz.canvas.convert_from_pixels(hadj.value, vadj.value)
+ x2, y2 = self.viz.canvas.convert_from_pixels(hadj.value + hadj.page_size, vadj.value + vadj.page_size)
+ line_width = 5.0/self.viz.zoom.value
+
+ # draw the horizontal axis
+ self.hlines.set_property("line-width", line_width)
+ yc = y2 - line_width/2
+
+ sim_x1 = x1/core.PIXELS_PER_METER
+ sim_x2 = x2/core.PIXELS_PER_METER
+ x0, xdiv = self._compute_divisions(sim_x1, sim_x2)
+ path = ["M %r %r L %r %r" % (x1, yc, x2, yc)]
+ x = x0
+ while x < sim_x2:
+ path.append("M %r %r L %r %r" % (core.PIXELS_PER_METER*x, yc - offset, core.PIXELS_PER_METER*x, yc))
+ label = get_label()
+ label.set_properties(font=("Sans Serif %f" % int(12/zoom)),
+ text=("%G" % x),
+ fill_color_rgba=self.color,
+ alignment=pango.ALIGN_CENTER,
+ anchor=gtk.ANCHOR_S,
+ x=core.PIXELS_PER_METER*x,
+ y=(yc - offset))
+ x += xdiv
+ del x
+
+ self.hlines.set_property("data", " ".join(path))
+
+ # draw the vertical axis
+ self.vlines.set_property("line-width", line_width)
+ xc = x1 + line_width/2
+
+ sim_y1 = y1/core.PIXELS_PER_METER
+ sim_y2 = y2/core.PIXELS_PER_METER
+
+
+ y0, ydiv = self._compute_divisions(sim_y1, sim_y2)
+ path = ["M %r %r L %r %r" % (xc, y1, xc, y2)]
+ y = y0
+ while y < sim_y2:
+ path.append("M %r %r L %r %r" % (xc, core.PIXELS_PER_METER*y, xc + offset, core.PIXELS_PER_METER*y))
+ label = get_label()
+ label.set_properties(font=("Sans Serif %f" % int(12/zoom)),
+ text=("%G" % y),
+ fill_color_rgba=self.color,
+ alignment=pango.ALIGN_LEFT,
+ anchor=gtk.ANCHOR_W,
+ x=xc + offset,
+ y=core.PIXELS_PER_METER*y)
+ y += ydiv
+
+ self.vlines.set_property("data", " ".join(path))
+
+
+
+ self.labels.extend(unused_labels)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/ipython_view.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,296 @@
+"""
+Backend to the console plugin.
+
+@author: Eitan Isaacson
+@organization: IBM Corporation
+@copyright: Copyright (c) 2007 IBM Corporation
+@license: BSD
+
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the BSD which accompanies this distribution, and
+is available at U{http://www.opensource.org/licenses/bsd-license.php}
+"""
+# this file is a modified version of source code from the Accerciser project
+# http://live.gnome.org/accerciser
+
+import gtk
+import re
+import sys
+import os
+import pango
+from StringIO import StringIO
+import IPython
+
+ansi_colors = {'0;30': 'Black',
+ '0;31': 'Red',
+ '0;32': 'Green',
+ '0;33': 'Brown',
+ '0;34': 'Blue',
+ '0;35': 'Purple',
+ '0;36': 'Cyan',
+ '0;37': 'LightGray',
+ '1;30': 'DarkGray',
+ '1;31': 'DarkRed',
+ '1;32': 'SeaGreen',
+ '1;33': 'Yellow',
+ '1;34': 'LightBlue',
+ '1;35': 'MediumPurple',
+ '1;36': 'LightCyan',
+ '1;37': 'White'}
+
+class IterableIPShell:
+ def __init__(self,argv=None,user_ns=None,user_global_ns=None,
+ cin=None, cout=None,cerr=None, input_func=None):
+ if input_func:
+ IPython.iplib.raw_input_original = input_func
+ if cin:
+ IPython.Shell.Term.cin = cin
+ if cout:
+ IPython.Shell.Term.cout = cout
+ if cerr:
+ IPython.Shell.Term.cerr = cerr
+
+ if argv is None:
+ argv=[]
+
+ # This is to get rid of the blockage that occurs during
+ # IPython.Shell.InteractiveShell.user_setup()
+ IPython.iplib.raw_input = lambda x: None
+
+ self.term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
+ os.environ['TERM'] = 'dumb'
+ excepthook = sys.excepthook
+ self.IP = IPython.Shell.make_IPython(argv,user_ns=user_ns,
+ user_global_ns=user_global_ns,
+ embedded=True,
+ shell_class=IPython.Shell.InteractiveShell)
+ self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd),
+ header='IPython system call: ',
+ verbose=self.IP.rc.system_verbose)
+ sys.excepthook = excepthook
+ self.iter_more = 0
+ self.history_level = 0
+ self.complete_sep = re.compile('[\s\{\}\[\]\(\)]')
+
+ def execute(self):
+ self.history_level = 0
+ orig_stdout = sys.stdout
+ sys.stdout = IPython.Shell.Term.cout
+ try:
+ line = self.IP.raw_input(None, self.iter_more)
+ if self.IP.autoindent:
+ self.IP.readline_startup_hook(None)
+ except KeyboardInterrupt:
+ self.IP.write('\nKeyboardInterrupt\n')
+ self.IP.resetbuffer()
+ # keep cache in sync with the prompt counter:
+ self.IP.outputcache.prompt_count -= 1
+
+ if self.IP.autoindent:
+ self.IP.indent_current_nsp = 0
+ self.iter_more = 0
+ except:
+ self.IP.showtraceback()
+ else:
+ self.iter_more = self.IP.push(line)
+ if (self.IP.SyntaxTB.last_syntax_error and
+ self.IP.rc.autoedit_syntax):
+ self.IP.edit_syntax_error()
+ if self.iter_more:
+ self.prompt = str(self.IP.outputcache.prompt2).strip()
+ if self.IP.autoindent:
+ self.IP.readline_startup_hook(self.IP.pre_readline)
+ else:
+ self.prompt = str(self.IP.outputcache.prompt1).strip()
+ sys.stdout = orig_stdout
+
+ def historyBack(self):
+ self.history_level -= 1
+ return self._getHistory()
+
+ def historyForward(self):
+ self.history_level += 1
+ return self._getHistory()
+
+ def _getHistory(self):
+ try:
+ rv = self.IP.user_ns['In'][self.history_level].strip('\n')
+ except IndexError:
+ self.history_level = 0
+ rv = ''
+ return rv
+
+ def updateNamespace(self, ns_dict):
+ self.IP.user_ns.update(ns_dict)
+
+ def complete(self, line):
+ split_line = self.complete_sep.split(line)
+ possibilities = self.IP.complete(split_line[-1])
+ if possibilities:
+ common_prefix = reduce(self._commonPrefix, possibilities)
+ completed = line[:-len(split_line[-1])]+common_prefix
+ else:
+ completed = line
+ return completed, possibilities
+
+ def _commonPrefix(self, str1, str2):
+ for i in range(len(str1)):
+ if not str2.startswith(str1[:i+1]):
+ return str1[:i]
+ return str1
+
+ def shell(self, cmd,verbose=0,debug=0,header=''):
+ stat = 0
+ if verbose or debug: print header+cmd
+ # flush stdout so we don't mangle python's buffering
+ if not debug:
+ input, output = os.popen4(cmd)
+ print output.read()
+ output.close()
+ input.close()
+
+class ConsoleView(gtk.TextView):
+ def __init__(self):
+ gtk.TextView.__init__(self)
+ self.modify_font(pango.FontDescription('Mono'))
+ self.set_cursor_visible(True)
+ self.text_buffer = self.get_buffer()
+ self.mark = self.text_buffer.create_mark('scroll_mark',
+ self.text_buffer.get_end_iter(),
+ False)
+ for code in ansi_colors:
+ self.text_buffer.create_tag(code,
+ foreground=ansi_colors[code],
+ weight=700)
+ self.text_buffer.create_tag('0')
+ self.text_buffer.create_tag('notouch', editable=False)
+ self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
+ self.line_start = \
+ self.text_buffer.create_mark('line_start',
+ self.text_buffer.get_end_iter(), True
+ )
+ self.connect('key-press-event', self._onKeypress)
+ self.last_cursor_pos = 0
+
+ def write(self, text, editable=False):
+ segments = self.color_pat.split(text)
+ segment = segments.pop(0)
+ start_mark = self.text_buffer.create_mark(None,
+ self.text_buffer.get_end_iter(),
+ True)
+ self.text_buffer.insert(self.text_buffer.get_end_iter(), segment)
+
+ if segments:
+ ansi_tags = self.color_pat.findall(text)
+ for tag in ansi_tags:
+ i = segments.index(tag)
+ self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(),
+ segments[i+1], tag)
+ segments.pop(i)
+ if not editable:
+ self.text_buffer.apply_tag_by_name('notouch',
+ self.text_buffer.get_iter_at_mark(start_mark),
+ self.text_buffer.get_end_iter())
+ self.text_buffer.delete_mark(start_mark)
+ self.scroll_mark_onscreen(self.mark)
+
+ def showPrompt(self, prompt):
+ self.write(prompt)
+ self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter())
+
+ def changeLine(self, text):
+ iter = self.text_buffer.get_iter_at_mark(self.line_start)
+ iter.forward_to_line_end()
+ self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter)
+ self.write(text, True)
+
+ def getCurrentLine(self):
+ rv = self.text_buffer.get_slice(self.text_buffer.get_iter_at_mark(self.line_start),
+ self.text_buffer.get_end_iter(), False)
+ return rv
+
+ def showReturned(self, text):
+ iter = self.text_buffer.get_iter_at_mark(self.line_start)
+ iter.forward_to_line_end()
+ self.text_buffer.apply_tag_by_name('notouch',
+ self.text_buffer.get_iter_at_mark(self.line_start),
+ iter)
+ self.write('\n'+text)
+ if text:
+ self.write('\n')
+ self.showPrompt(self.prompt)
+ self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter())
+ self.text_buffer.place_cursor(self.text_buffer.get_end_iter())
+
+ def _onKeypress(self, obj, event):
+ if not event.string:
+ return
+ insert_mark = self.text_buffer.get_insert()
+ insert_iter = self.text_buffer.get_iter_at_mark(insert_mark)
+ selection_mark = self.text_buffer.get_selection_bound()
+ selection_iter = self.text_buffer.get_iter_at_mark(selection_mark)
+ start_iter = self.text_buffer.get_iter_at_mark(self.line_start)
+ if start_iter.compare(insert_iter) <= 0 and \
+ start_iter.compare(selection_iter) <= 0:
+ return
+ elif start_iter.compare(insert_iter) > 0 and \
+ start_iter.compare(selection_iter) > 0:
+ self.text_buffer.place_cursor(start_iter)
+ elif insert_iter.compare(selection_iter) < 0:
+ self.text_buffer.move_mark(insert_mark, start_iter)
+ elif insert_iter.compare(selection_iter) > 0:
+ self.text_buffer.move_mark(selection_mark, start_iter)
+
+
+class IPythonView(ConsoleView, IterableIPShell):
+ def __init__(self):
+ ConsoleView.__init__(self)
+ self.cout = StringIO()
+ IterableIPShell.__init__(self, cout=self.cout,cerr=self.cout,
+ input_func=self.raw_input)
+ self.connect('key_press_event', self.keyPress)
+ self.execute()
+ self.cout.truncate(0)
+ self.showPrompt(self.prompt)
+ self.interrupt = False
+
+ def raw_input(self, prompt=''):
+ if self.interrupt:
+ self.interrupt = False
+ raise KeyboardInterrupt
+ return self.getCurrentLine()
+
+ def keyPress(self, widget, event):
+ if event.state & gtk.gdk.CONTROL_MASK and event.keyval == 99:
+ self.interrupt = True
+ self._processLine()
+ return True
+ elif event.keyval == gtk.keysyms.Return:
+ self._processLine()
+ return True
+ elif event.keyval == gtk.keysyms.Up:
+ self.changeLine(self.historyBack())
+ return True
+ elif event.keyval == gtk.keysyms.Down:
+ self.changeLine(self.historyForward())
+ return True
+ elif event.keyval == gtk.keysyms.Tab:
+ if not self.getCurrentLine().strip():
+ return False
+ completed, possibilities = self.complete(self.getCurrentLine())
+ if len(possibilities) > 1:
+ slice = self.getCurrentLine()
+ self.write('\n')
+ for symbol in possibilities:
+ self.write(symbol+'\n')
+ self.showPrompt(self.prompt)
+ self.changeLine(completed or slice)
+ return True
+
+ def _processLine(self):
+ self.history_pos = 0
+ self.execute()
+ rv = self.cout.getvalue()
+ if rv: rv = rv.strip('\n')
+ self.showReturned(rv)
+ self.cout.truncate(0)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/plugins/interface_statistics.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,167 @@
+import gtk
+import ns3
+from visualizer.base import InformationWindow
+
+NODE_STATISTICS_MEMORY = 10
+
+
+class StatisticsCollector(object):
+ """
+ Collects interface statistics for all nodes.
+ """
+
+ class NetDevStats(object):
+ __slots__ = ['rxPackets', 'rxBytes', 'txPackets', 'txBytes',
+ 'rxPacketRate', 'rxBitRate', 'txPacketRate', 'txBitRate']
+
+ def __init__(self, visualizer):
+ self.node_statistics = {} # nodeid -> list(raw statistics)
+ self.visualizer = visualizer
+
+ def simulation_periodic_update(self, viz):
+ nodes_statistics = viz.simulation.sim_helper.GetNodesStatistics()
+ for stats in nodes_statistics:
+ try:
+ raw_stats_list = self.node_statistics[stats.nodeId]
+ except KeyError:
+ raw_stats_list = []
+ self.node_statistics[stats.nodeId] = raw_stats_list
+ raw_stats_list.append(stats.statistics)
+ while len(raw_stats_list) > NODE_STATISTICS_MEMORY:
+ raw_stats_list.pop(0)
+
+ def get_interface_statistics(self, nodeId):
+ try:
+ raw_stats_list = self.node_statistics[nodeId]
+ except KeyError:
+ return []
+
+ if len(raw_stats_list) < NODE_STATISTICS_MEMORY:
+ return []
+ assert len(raw_stats_list) == NODE_STATISTICS_MEMORY
+ tx_packets1 = [] # transmitted packets, one value per interface
+ rx_packets1 = []
+ tx_bytes1 = []
+ rx_bytes1 = []
+ for iface, stats in enumerate(raw_stats_list[0]):
+ tx_packets1.append(stats.transmittedPackets)
+ tx_bytes1.append(stats.transmittedBytes)
+ rx_packets1.append(stats.receivedPackets)
+ rx_bytes1.append(stats.receivedBytes)
+
+ retval = []
+
+ k = self.visualizer.sample_period*(NODE_STATISTICS_MEMORY-1)
+ for iface, stats in enumerate(raw_stats_list[-1]):
+ outStat = self.NetDevStats()
+ outStat.txPackets = stats.transmittedPackets
+ outStat.txBytes = stats.transmittedBytes
+ outStat.rxPackets = stats.receivedPackets
+ outStat.rxBytes = stats.receivedBytes
+
+ outStat.txPacketRate = (stats.transmittedPackets - tx_packets1[iface])/k
+ outStat.rxPacketRate = (stats.receivedPackets - rx_packets1[iface])/k
+ outStat.txBitRate = (stats.transmittedBytes - tx_bytes1[iface])*8/k
+ outStat.rxBitRate = (stats.receivedBytes - rx_bytes1[iface])*8/k
+ retval.append(outStat)
+ return retval
+
+
+class ShowInterfaceStatistics(InformationWindow):
+ (
+ COLUMN_INTERFACE,
+
+ COLUMN_TX_PACKETS,
+ COLUMN_TX_BYTES,
+ COLUMN_TX_PACKET_RATE,
+ COLUMN_TX_BIT_RATE,
+
+ COLUMN_RX_PACKETS,
+ COLUMN_RX_BYTES,
+ COLUMN_RX_PACKET_RATE,
+ COLUMN_RX_BIT_RATE,
+
+ ) = range(9)
+
+ def __init__(self, visualizer, node_index, statistics_collector):
+ InformationWindow.__init__(self)
+ self.win = gtk.Dialog(parent=visualizer.window,
+ flags=gtk.DIALOG_DESTROY_WITH_PARENT|gtk.DIALOG_NO_SEPARATOR,
+ buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
+ self.win.connect("response", self._response_cb)
+ self.win.set_title("Statistics for node %i" % node_index)
+ self.visualizer = visualizer
+ self.statistics_collector = statistics_collector
+ self.node_index = node_index
+ self.viz_node = visualizer.get_node(node_index)
+
+ self.table_model = gtk.ListStore(*([str]*13))
+
+ treeview = gtk.TreeView(self.table_model)
+ treeview.show()
+ self.win.vbox.add(treeview)
+
+ def add_column(descr, colid):
+ column = gtk.TreeViewColumn(descr, gtk.CellRendererText(), text=colid)
+ treeview.append_column(column)
+
+ add_column("Interface", self.COLUMN_INTERFACE)
+
+ add_column("Tx Packets", self.COLUMN_TX_PACKETS)
+ add_column("Tx Bytes", self.COLUMN_TX_BYTES)
+ add_column("Tx pkt/1s", self.COLUMN_TX_PACKET_RATE)
+ add_column("Tx bit/1s", self.COLUMN_TX_BIT_RATE)
+
+ add_column("Rx Packets", self.COLUMN_RX_PACKETS)
+ add_column("Rx Bytes", self.COLUMN_RX_BYTES)
+ add_column("Rx pkt/1s", self.COLUMN_RX_PACKET_RATE)
+ add_column("Rx bit/1s", self.COLUMN_RX_BIT_RATE)
+
+ self.visualizer.add_information_window(self)
+ self.win.show()
+
+ def _response_cb(self, win, response):
+ self.win.destroy()
+ self.visualizer.remove_information_window(self)
+
+ def update(self):
+ node = ns3.NodeList.GetNode(self.node_index)
+ stats_list = self.statistics_collector.get_interface_statistics(self.node_index)
+ self.table_model.clear()
+ for iface, stats in enumerate(stats_list):
+ tree_iter = self.table_model.append()
+ netdevice = node.GetDevice(iface)
+ interface_name = ns3.Names.FindName(netdevice)
+ if not interface_name:
+ interface_name = "(interface %i)" % iface
+ self.table_model.set(tree_iter,
+ self.COLUMN_INTERFACE, interface_name,
+
+ self.COLUMN_TX_PACKETS, str(stats.txPackets),
+ self.COLUMN_TX_BYTES, str(stats.txBytes),
+ self.COLUMN_TX_PACKET_RATE, str(stats.txPacketRate),
+ self.COLUMN_TX_BIT_RATE, str(stats.txBitRate),
+
+ self.COLUMN_RX_PACKETS, str(stats.rxPackets),
+ self.COLUMN_RX_BYTES, str(stats.rxBytes),
+ self.COLUMN_RX_PACKET_RATE, str(stats.rxPacketRate),
+ self.COLUMN_RX_BIT_RATE, str(stats.rxBitRate)
+ )
+
+
+def populate_node_menu(viz, node, menu, statistics_collector):
+
+ menu_item = gtk.MenuItem("Show Interface Statistics")
+ menu_item.show()
+
+ def _show_it(dummy_menu_item):
+ ShowInterfaceStatistics(viz, node.node_index, statistics_collector)
+
+ menu_item.connect("activate", _show_it)
+ menu.add(menu_item)
+
+
+def register(viz):
+ statistics_collector = StatisticsCollector(viz)
+ viz.connect("populate-node-menu", populate_node_menu, statistics_collector)
+ viz.connect("simulation-periodic-update", statistics_collector.simulation_periodic_update)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/plugins/ipv4_routing_table.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,95 @@
+import gtk
+import ns3
+from visualizer.base import InformationWindow
+
+class ShowIpv4RoutingTable(InformationWindow):
+ (
+ COLUMN_DESTINATION,
+ COLUMN_NEXT_HOP,
+ COLUMN_INTERFACE,
+ ) = range(3)
+
+ def __init__(self, visualizer, node_index):
+ InformationWindow.__init__(self)
+ self.win = gtk.Dialog(parent=visualizer.window,
+ flags=gtk.DIALOG_DESTROY_WITH_PARENT|gtk.DIALOG_NO_SEPARATOR,
+ buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
+ self.win.connect("response", self._response_cb)
+ self.win.set_title("IPv4 routing table for node %i" % node_index)
+ self.visualizer = visualizer
+ self.node_index = node_index
+
+ self.table_model = gtk.ListStore(str, str, str)
+
+ treeview = gtk.TreeView(self.table_model)
+ treeview.show()
+ self.win.vbox.add(treeview)
+
+ # Dest.
+ column = gtk.TreeViewColumn('Destination', gtk.CellRendererText(),
+ text=self.COLUMN_DESTINATION)
+ treeview.append_column(column)
+
+ # Next hop
+ column = gtk.TreeViewColumn('Next hop', gtk.CellRendererText(),
+ text=self.COLUMN_NEXT_HOP)
+ treeview.append_column(column)
+
+ # Interface
+ column = gtk.TreeViewColumn('Interface', gtk.CellRendererText(),
+ text=self.COLUMN_INTERFACE)
+ treeview.append_column(column)
+
+ self.visualizer.add_information_window(self)
+ self.win.show()
+
+ def _response_cb(self, win, response):
+ self.win.destroy()
+ self.visualizer.remove_information_window(self)
+
+ def update(self):
+ node = ns3.NodeList.GetNode(self.node_index)
+ ipv4 = node.GetObject(ns3.Ipv4.GetTypeId())
+ routing = ipv4.GetRoutingProtocol()
+ if routing is None:
+ return
+ if isinstance(routing, ns3.Ipv4StaticRouting):
+ ipv4_routing = routing
+ elif isinstance(routing, ns3.Ipv4ListRouting):
+ for rI in range(routing.GetNRoutingProtocols()):
+ ipv4_routing, prio = routing.GetRoutingProtocol(rI)
+ if isinstance(ipv4_routing, ns3.Ipv4StaticRouting):
+ break
+ else:
+ return
+ else:
+ return
+ self.table_model.clear()
+ for routeI in range(ipv4_routing.GetNRoutes()):
+ route = ipv4_routing.GetRoute(routeI)
+ tree_iter = self.table_model.append()
+ netdevice = ipv4.GetNetDevice(route.GetInterface())
+ if netdevice is None:
+ interface_name = 'lo'
+ else:
+ interface_name = ns3.Names.FindName(netdevice)
+ if not interface_name:
+ interface_name = "(interface %i)" % route.GetInterface()
+ self.table_model.set(tree_iter,
+ self.COLUMN_DESTINATION, str(route.GetDest()),
+ self.COLUMN_NEXT_HOP, str(route.GetGateway()),
+ self.COLUMN_INTERFACE, interface_name)
+
+
+def populate_node_menu(viz, node, menu):
+ menu_item = gtk.MenuItem("Show IPv4 Routing Table")
+ menu_item.show()
+
+ def _show_ipv4_routing_table(dummy_menu_item):
+ ShowIpv4RoutingTable(viz, node.node_index)
+
+ menu_item.connect("activate", _show_ipv4_routing_table)
+ menu.add(menu_item)
+
+def register(viz):
+ viz.connect("populate-node-menu", populate_node_menu)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/plugins/olsr.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,102 @@
+import gtk
+import ns3
+from visualizer.base import InformationWindow
+
+class ShowOlsrRoutingTable(InformationWindow):
+ (
+ COLUMN_DESTINATION,
+ COLUMN_NEXT_HOP,
+ COLUMN_INTERFACE,
+ COLUMN_NUM_HOPS,
+ ) = range(4)
+
+ def __init__(self, visualizer, node_index):
+ InformationWindow.__init__(self)
+ self.win = gtk.Dialog(parent=visualizer.window,
+ flags=gtk.DIALOG_DESTROY_WITH_PARENT|gtk.DIALOG_NO_SEPARATOR,
+ buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
+ self.win.set_default_size(gtk.gdk.screen_width()/2, gtk.gdk.screen_height()/2)
+ self.win.connect("response", self._response_cb)
+ self.win.set_title("OLSR routing table for node %i" % node_index)
+ self.visualizer = visualizer
+ self.node_index = node_index
+
+ self.table_model = gtk.ListStore(str, str, str, int)
+
+ treeview = gtk.TreeView(self.table_model)
+ treeview.show()
+ sw = gtk.ScrolledWindow()
+ sw.set_properties(hscrollbar_policy=gtk.POLICY_AUTOMATIC,
+ vscrollbar_policy=gtk.POLICY_AUTOMATIC)
+ sw.show()
+ sw.add(treeview)
+ self.win.vbox.add(sw)
+
+ # Dest.
+ column = gtk.TreeViewColumn('Destination', gtk.CellRendererText(),
+ text=self.COLUMN_DESTINATION)
+ treeview.append_column(column)
+
+ # Next hop
+ column = gtk.TreeViewColumn('Next hop', gtk.CellRendererText(),
+ text=self.COLUMN_NEXT_HOP)
+ treeview.append_column(column)
+
+ # Interface
+ column = gtk.TreeViewColumn('Interface', gtk.CellRendererText(),
+ text=self.COLUMN_INTERFACE)
+ treeview.append_column(column)
+
+ # Num. Hops
+ column = gtk.TreeViewColumn('Num. Hops', gtk.CellRendererText(),
+ text=self.COLUMN_NUM_HOPS)
+ treeview.append_column(column)
+
+ self.visualizer.add_information_window(self)
+ self.win.show()
+
+ def _response_cb(self, win, response):
+ self.win.destroy()
+ self.visualizer.remove_information_window(self)
+
+ def update(self):
+ node = ns3.NodeList.GetNode(self.node_index)
+ olsr = node.GetObject(ns3.olsr.RoutingProtocol.GetTypeId())
+ ipv4 = node.GetObject(ns3.Ipv4.GetTypeId())
+ if olsr is None:
+ return
+ self.table_model.clear()
+ for route in olsr.GetRoutingTableEntries():
+ tree_iter = self.table_model.append()
+ netdevice = ipv4.GetNetDevice(route.interface)
+ if netdevice is None:
+ interface_name = 'lo'
+ else:
+ interface_name = ns3.Names.FindName(netdevice)
+ if not interface_name:
+ interface_name = "(interface %i)" % route.interface
+ self.table_model.set(tree_iter,
+ self.COLUMN_DESTINATION, str(route.destAddr),
+ self.COLUMN_NEXT_HOP, str(route.nextAddr),
+ self.COLUMN_INTERFACE, interface_name,
+ self.COLUMN_NUM_HOPS, route.distance)
+
+
+def populate_node_menu(viz, node, menu):
+ ns3_node = ns3.NodeList.GetNode(node.node_index)
+ olsr = ns3_node.GetObject(ns3.olsr.RoutingProtocol.GetTypeId())
+ if olsr is None:
+ print "No OLSR"
+ return
+
+ menu_item = gtk.MenuItem("Show OLSR Routing Table")
+ menu_item.show()
+
+ def _show_ipv4_routing_table(dummy_menu_item):
+ ShowOlsrRoutingTable(viz, node.node_index)
+
+ menu_item.connect("activate", _show_ipv4_routing_table)
+ menu.add(menu_item)
+
+def register(viz):
+ viz.connect("populate-node-menu", populate_node_menu)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/plugins/show_last_packets.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,231 @@
+import gobject
+import gtk
+import ns3
+from visualizer.base import InformationWindow
+from visualizer.higcontainer import HIGContainer
+from kiwi.ui.objectlist import ObjectList, Column
+
+class ShowLastPackets(InformationWindow):
+ class PacketList(gtk.ScrolledWindow):
+ (
+ COLUMN_TIME,
+ COLUMN_INTERFACE,
+ COLUMN_SIZE,
+ COLUMN_CONTENTS,
+ ) = range(4)
+
+ def __init__(self):
+ super(ShowLastPackets.PacketList, self).__init__()
+ self.set_properties(hscrollbar_policy=gtk.POLICY_AUTOMATIC,
+ vscrollbar_policy=gtk.POLICY_AUTOMATIC)
+ self.table_model = gtk.ListStore(*([str]*4))
+ treeview = gtk.TreeView(self.table_model)
+ treeview.show()
+ self.add(treeview)
+
+ def add_column(descr, colid):
+ column = gtk.TreeViewColumn(descr, gtk.CellRendererText(), text=colid)
+ treeview.append_column(column)
+
+ add_column("Time", self.COLUMN_TIME)
+ add_column("Interface", self.COLUMN_INTERFACE)
+ add_column("Size", self.COLUMN_SIZE)
+ add_column("Contents", self.COLUMN_CONTENTS)
+
+ def update(self, node, packet_list):
+ self.table_model.clear()
+ for sample in packet_list:
+ tree_iter = self.table_model.append()
+ if sample.device is None:
+ interface_name = "(unknown)"
+ else:
+ interface_name = ns3.Names.FindName(sample.device)
+ if not interface_name:
+ interface_name = "(interface %i)" % sample.device.GetIfIndex()
+ self.table_model.set(tree_iter,
+ self.COLUMN_TIME, str(sample.time.GetSeconds()),
+ self.COLUMN_INTERFACE, interface_name,
+ self.COLUMN_SIZE, str(sample.packet.GetSize ()),
+ self.COLUMN_CONTENTS, str(sample.packet)
+ )
+
+
+ def __init__(self, visualizer, node_index):
+ InformationWindow.__init__(self)
+ self.win = gtk.Dialog(parent=visualizer.window,
+ flags=gtk.DIALOG_DESTROY_WITH_PARENT|gtk.DIALOG_NO_SEPARATOR,
+ buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
+ self.win.connect("response", self._response_cb)
+ self.win.set_title("Last packets for node %i" % node_index)
+ self.visualizer = visualizer
+ self.viz_node = visualizer.get_node(node_index)
+ self.node = ns3.NodeList.GetNode(node_index)
+
+ def smart_expand(expander, vbox):
+ if expander.get_expanded():
+ vbox.set_child_packing(expander, expand=True, fill=True, padding=0, pack_type=gtk.PACK_START)
+ else:
+ vbox.set_child_packing(expander, expand=False, fill=False, padding=0, pack_type=gtk.PACK_START)
+
+ main_hbox = gtk.HBox(False, 4)
+ main_hbox.show()
+ main_vbox = gtk.VBox(False, 4)
+ main_vbox.show()
+ self.win.vbox.add(main_hbox)
+ main_hbox.add(main_vbox)
+
+ self.tx_list = self.PacketList()
+ self.tx_list.show()
+ group = gtk.Expander("Last transmitted packets")
+ group.show()
+ group.add(self.tx_list)
+ main_vbox.pack_start(group, expand=False, fill=False)
+ group.connect_after("activate", smart_expand, main_vbox)
+
+ self.rx_list = self.PacketList()
+ self.rx_list.show()
+ group = gtk.Expander("Last received packets")
+ group.show()
+ group.add(self.rx_list)
+ main_vbox.pack_start(group, expand=False, fill=False)
+ group.connect_after("activate", smart_expand, main_vbox)
+
+ self.drop_list = self.PacketList()
+ self.drop_list.show()
+ group = gtk.Expander("Last dropped packets")
+ group.show()
+ group.add(self.drop_list)
+ main_vbox.pack_start(group, expand=False, fill=False)
+ group.connect_after("activate", smart_expand, main_vbox)
+
+
+ # Packet Filter
+
+ # - options
+ self.packet_capture_options = ns3.PyViz.PacketCaptureOptions()
+ self.packet_capture_options.numLastPackets = 100
+
+ packet_filter_vbox = gtk.VBox(False, 4)
+ packet_filter_vbox.show()
+ main_hbox.add(packet_filter_vbox)
+
+ sel_buttons_box = gtk.HButtonBox()
+ sel_buttons_box.show()
+ packet_filter_vbox.pack_start(sel_buttons_box, False, False, 4)
+ select_all_button = gobject.new(gtk.Button, label="Sel. All", visible=True)
+ select_none_button = gobject.new(gtk.Button, label="Sel. None", visible=True)
+ sel_buttons_box.add(select_all_button)
+ sel_buttons_box.add(select_none_button)
+
+ self.packet_filter_widget = ObjectList([
+ Column('selected', title="Sel.", data_type=bool, editable=True),
+ Column('name', title="Header"),
+ ], sortable=True)
+ self.packet_filter_widget.show()
+ packet_filter_vbox.pack_start(self.packet_filter_widget, True, True, 4)
+
+ class TypeIdConfig(object):
+ __slots__ = ['name', 'selected', 'typeid']
+
+ self.packet_filter_list = [] # list of TypeIdConfig instances
+
+ Header = ns3.TypeId.LookupByName("ns3::Header")
+ Trailer = ns3.TypeId.LookupByName("ns3::Trailer")
+ for typeid_i in range(ns3.TypeId.GetRegisteredN()):
+ typeid = ns3.TypeId.GetRegistered(typeid_i)
+ # check if this is a header or trailer subtype
+ typeid_tmp = typeid
+ type_is_good = False
+ while 1:
+ if typeid_tmp == Header or typeid_tmp == Trailer:
+ type_is_good = True
+ break
+ if typeid_tmp.HasParent():
+ typeid_tmp = typeid_tmp.GetParent()
+ else:
+ break
+ if not type_is_good:
+ continue
+ if typeid in [Header, Trailer]:
+ continue
+ c = TypeIdConfig()
+ c.selected = True
+ c.name = typeid.GetName()
+ c.typeid = typeid
+ self.packet_filter_list.append(c)
+ self.packet_filter_widget.add_list(self.packet_filter_list)
+
+ def update_capture_options():
+ if self.op_AND_button.props.active:
+ self.packet_capture_options.mode = ns3.PyViz.PACKET_CAPTURE_FILTER_HEADERS_AND
+ else:
+ self.packet_capture_options.mode = ns3.PyViz.PACKET_CAPTURE_FILTER_HEADERS_OR
+ self.packet_capture_options.numLastPackets = 100
+ self.packet_capture_options.headers = [c.typeid for c in self.packet_filter_list if c.selected]
+ self.visualizer.simulation.lock.acquire()
+ try:
+ self.visualizer.simulation.sim_helper.SetPacketCaptureOptions(
+ self.node.GetId(), self.packet_capture_options)
+ finally:
+ self.visualizer.simulation.lock.release()
+
+ def sel_all_cb(bt):
+ for c in self.packet_filter_list:
+ c.selected = True
+ self.packet_filter_widget.refresh()
+ update_capture_options()
+
+ def sel_none_cb(bt):
+ for c in self.packet_filter_list:
+ c.selected = False
+ self.packet_filter_widget.refresh()
+ update_capture_options()
+
+ select_all_button.connect("clicked", sel_all_cb)
+ select_none_button.connect("clicked", sel_none_cb)
+
+ op_buttons_box = gtk.HButtonBox()
+ op_buttons_box.show()
+ packet_filter_vbox.pack_start(op_buttons_box, False, False, 4)
+ self.op_AND_button = gobject.new(gtk.RadioButton, label="AND", visible=True)
+ self.op_OR_button = gobject.new(gtk.RadioButton, label="OR", visible=True, group=self.op_AND_button)
+ op_buttons_box.add(self.op_AND_button)
+ op_buttons_box.add(self.op_OR_button)
+ self.op_OR_button.props.active = True
+
+ self.op_AND_button.connect("toggled", lambda b: update_capture_options())
+
+ def cell_edited(l, obj, attribute):
+ update_capture_options()
+ self.packet_filter_widget.connect("cell-edited", cell_edited)
+
+ update_capture_options()
+
+ self.visualizer.add_information_window(self)
+ self.win.set_default_size(600, 300)
+ self.win.show()
+
+ def _response_cb(self, win, response):
+ self.win.destroy()
+ self.visualizer.remove_information_window(self)
+
+ def update(self):
+ last_packets = self.visualizer.simulation.sim_helper.GetLastPackets(self.node.GetId())
+
+ self.tx_list.update(self.node, last_packets.lastTransmittedPackets)
+ self.rx_list.update(self.node, last_packets.lastReceivedPackets)
+ self.drop_list.update(self.node, last_packets.lastDroppedPackets)
+
+
+def populate_node_menu(viz, node, menu):
+ menu_item = gtk.MenuItem("Show Last Packets")
+ menu_item.show()
+
+ def _show_it(dummy_menu_item):
+ ShowLastPackets(viz, node.node_index)
+
+ menu_item.connect("activate", _show_it)
+ menu.add(menu_item)
+
+def register(viz):
+ viz.connect("populate-node-menu", populate_node_menu)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/plugins/wifi_intrastructure_link.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,117 @@
+import math
+import ns3
+import goocanvas
+from visualizer.base import Link, transform_distance_canvas_to_simulation
+
+class WifiLink(Link):
+ def __init__(self, parent_canvas_item, sta, dev):
+ self.node1 = sta
+ self.dev = dev
+ self.node2 = None # ap
+ self.canvas_item = goocanvas.Group(parent=parent_canvas_item)
+ self.invisible_line = goocanvas.Polyline(parent=self.canvas_item,
+ line_width=25.0,
+ visibility=goocanvas.ITEM_HIDDEN)
+ self.visible_line = goocanvas.Polyline(parent=self.canvas_item,
+ line_width=1.0,
+ stroke_color_rgba=0xC00000FF,
+ line_dash=goocanvas.LineDash([2.0, 2.0 ]))
+ self.invisible_line.props.pointer_events = (goocanvas.EVENTS_STROKE_MASK
+ |goocanvas.EVENTS_FILL_MASK
+ |goocanvas.EVENTS_PAINTED_MASK)
+ self.canvas_item.set_data("pyviz-object", self)
+ self.canvas_item.lower(None)
+ self.set_ap(None)
+
+ def set_ap(self, ap):
+ if ap is self.node2:
+ return
+ if self.node2 is not None:
+ self.node2.remove_link(self)
+ self.node2 = ap
+ if self.node2 is None:
+ self.canvas_item.set_property("visibility", goocanvas.ITEM_HIDDEN)
+ else:
+ self.node2.add_link(self)
+ self.canvas_item.set_property("visibility", goocanvas.ITEM_VISIBLE)
+ self.update_points()
+
+ def update_points(self):
+ if self.node2 is None:
+ return
+ pos1_x, pos1_y = self.node1.get_position()
+ pos2_x, pos2_y = self.node2.get_position()
+ points = goocanvas.Points([(pos1_x, pos1_y), (pos2_x, pos2_y)])
+ self.visible_line.set_property("points", points)
+ self.invisible_line.set_property("points", points)
+
+ def destroy(self):
+ self.canvas_item.destroy()
+ self.node1 = None
+ self.node2 = None
+
+ def tooltip_query(self, tooltip):
+ pos1_x, pos1_y = self.node1.get_position()
+ pos2_x, pos2_y = self.node2.get_position()
+ dx = pos2_x - pos1_x
+ dy = pos2_y - pos1_y
+ d = transform_distance_canvas_to_simulation(math.sqrt(dx*dx + dy*dy))
+ mac = self.dev.GetMac()
+ tooltip.set_text(("WiFi link between STA Node %i and AP Node %i; distance=%.2f m.\n"
+ "SSID: %s\n"
+ "BSSID: %s")
+ % (self.node1.node_index, self.node2.node_index, d,
+ mac.GetSsid(), mac.GetBssid()))
+
+
+class WifiLinkMonitor(object):
+ def __init__(self, dummy_viz):
+ self.access_points = {} # bssid -> node
+ self.stations = [] # list of (sta_netdevice, viz_node, wifi_link)
+
+ def scan_nodes(self, viz):
+ for (sta_netdevice, viz_node, wifi_link) in self.stations:
+ wifi_link.destroy()
+
+ self.access_points = {}
+ self.stations = []
+
+ for node in viz.nodes.itervalues():
+ ns3_node = ns3.NodeList.GetNode(node.node_index)
+ for devI in range(ns3_node.GetNDevices()):
+ dev = ns3_node.GetDevice(devI)
+ if not isinstance(dev, ns3.WifiNetDevice):
+ continue
+ wifi_mac = dev.GetMac()
+ if isinstance(wifi_mac, ns3.NqstaWifiMac):
+ wifi_link = WifiLink(viz.links_group, node, dev)
+ self.stations.append((dev, node, wifi_link))
+ elif isinstance(wifi_mac, ns3.NqapWifiMac):
+ bssid = ns3.Mac48Address.ConvertFrom(dev.GetAddress())
+ self.access_points[str(bssid)] = node
+ #print "APs: ", self.access_points
+ #print "STAs: ", self.stations
+
+ def simulation_periodic_update(self, viz):
+ for (sta_netdevice, viz_node, wifi_link) in self.stations:
+ if not sta_netdevice.IsLinkUp():
+ wifi_link.set_ap(None)
+ continue
+ bssid = str(sta_netdevice.GetMac().GetBssid())
+ if bssid == '00:00:00:00:00:00':
+ wifi_link.set_ap(None)
+ continue
+ ap = self.access_points[bssid]
+ wifi_link.set_ap(ap)
+
+ def update_view(self, viz):
+ for (dummy_sta_netdevice, dummy_viz_node, wifi_link) in self.stations:
+ if wifi_link is not None:
+ wifi_link.update_points()
+
+
+def register(viz):
+ link_monitor = WifiLinkMonitor(viz)
+ viz.connect("simulation-periodic-update", link_monitor.simulation_periodic_update)
+ viz.connect("update-view", link_monitor.update_view)
+ viz.connect("topology-scanned", link_monitor.scan_nodes)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/resource/Basurero_Palm_Z22.svg Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,311 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="122.87033"
+ height="185.93257"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="Basurero_Palm_Z22.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient3237">
+ <stop
+ style="stop-color: rgb(156, 156, 156); stop-opacity: 1;"
+ offset="0"
+ id="stop3239" />
+ <stop
+ id="stop3245"
+ offset="0.5"
+ style="stop-color: rgb(220, 220, 220); stop-opacity: 1;" />
+ <stop
+ style="stop-color: rgb(168, 168, 168); stop-opacity: 1;"
+ offset="1"
+ id="stop3241" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2911">
+ <stop
+ style="stop-color: rgb(175, 193, 225); stop-opacity: 1;"
+ offset="0"
+ id="stop2913" />
+ <stop
+ style="stop-color: rgb(92, 144, 233); stop-opacity: 1;"
+ offset="1"
+ id="stop2915" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2911"
+ id="linearGradient3229"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-89.3787,74.9628)"
+ x1="157.91194"
+ y1="149.28284"
+ x2="151.2318"
+ y2="223.88245" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3237"
+ id="linearGradient3243"
+ x1="59.830921"
+ y1="241.51917"
+ x2="200.76509"
+ y2="179.29376"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-89.3787,74.9628)" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.59688333"
+ inkscape:cx="428.4488"
+ inkscape:cy="185.26469"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="1280"
+ inkscape:window-height="753"
+ inkscape:window-x="0"
+ inkscape:window-y="47"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/publicdomain/">
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Reproduction" />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Distribution" />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Capa 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-160.08253,-45.555814)">
+ <g
+ id="g3261"
+ inkscape:export-filename="/home/e288288/imagen/svg/palm_z22.png"
+ inkscape:export-xdpi="75"
+ inkscape:export-ydpi="75"
+ transform="translate(89.3787,-74.9628)">
+ <rect
+ ry="22.210958"
+ rx="22.210958"
+ y="120.88235"
+ x="71.067566"
+ height="185.20511"
+ width="122.14287"
+ id="rect2176"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.72746599;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <rect
+ style="fill:url(#linearGradient3243);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.72746599;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2856"
+ width="119.34706"
+ height="182.73219"
+ x="72.215469"
+ y="121.8688"
+ rx="20.444592"
+ ry="20.444592" />
+ <rect
+ ry="18.259377"
+ rx="18.259377"
+ y="124.17456"
+ x="74.340469"
+ height="178.12067"
+ width="115.09706"
+ id="rect3235"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:0.72746599;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <rect
+ ry="6.9516835"
+ rx="6.9516835"
+ y="135.9494"
+ x="84.825104"
+ height="125.89838"
+ width="94.6278"
+ id="rect2839"
+ style="fill:#e3e3e3;fill-opacity:1;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:0.72746599;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a4a4a4;stroke-width:0.72746599;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2841"
+ width="81.335205"
+ height="111.10477"
+ x="91.471397"
+ y="142.06659"
+ rx="1.7201494"
+ ry="1.7201494" />
+ <rect
+ ry="0"
+ rx="0"
+ y="143.58203"
+ x="92.920067"
+ height="79.936684"
+ width="78.437866"
+ id="rect2843"
+ style="fill:url(#linearGradient3229);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.72746599;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <g
+ id="g3257">
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#a2a2a2;stroke-width:0.72746599;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 73.133883,261.125 L 73.133883,279.3125 L 74.196383,279.3125 C 79.236007,279.3125 83.290129,275.25837 83.290133,270.21875 C 83.290133,265.17913 79.236011,261.125 74.196383,261.125 L 73.133883,261.125 z"
+ id="rect2875" />
+ <rect
+ style="fill:#164532;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.72746599;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2884"
+ width="1.8988454"
+ height="5.4946771"
+ x="78.096237"
+ y="267.47141" />
+ </g>
+ <g
+ transform="translate(-3.8147e-6,0)"
+ id="g3095">
+ <rect
+ ry="10.619836"
+ rx="10.619836"
+ y="272.72806"
+ x="89.104172"
+ height="21.239672"
+ width="86.069664"
+ id="rect2847"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1.10249996;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <path
+ id="rect2845"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1.10249996;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 129.26628,274.47992 L 135.01173,274.47992 C 139.92459,274.47992 143.87971,278.43504 143.87971,283.34789 C 143.87971,288.26075 139.92459,292.21587 135.01173,292.21587 L 129.26628,292.21587 C 124.35342,292.21587 120.39831,288.26075 120.39831,283.34789 C 120.39831,278.43504 124.35342,274.47992 129.26628,274.47992 z M 127.49374,268.95493 L 136.78426,268.95493 C 144.72845,268.95493 151.12396,275.37419 151.12396,283.34789 C 151.12396,291.3216 144.72845,297.74086 136.78426,297.74086 L 127.49374,297.74086 C 119.54955,297.74086 113.15404,291.3216 113.15404,283.34789 C 113.15404,275.37419 119.54955,268.95493 127.49374,268.95493 z" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1.17324996;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path2873"
+ sodipodi:cx="99.667305"
+ sodipodi:cy="282.88821"
+ sodipodi:rx="8.7876797"
+ sodipodi:ry="8.7876797"
+ d="M 108.45498,282.88821 A 8.7876797,8.7876797 0 1 1 90.879625,282.88821 A 8.7876797,8.7876797 0 1 1 108.45498,282.88821 z"
+ transform="matrix(0.939699,0,0,0.939699,5.30354,17.5183)" />
+ <path
+ transform="matrix(0.939699,0,0,0.939699,70.8358,17.5183)"
+ d="M 108.45498,282.88821 A 8.7876797,8.7876797 0 1 1 90.879625,282.88821 A 8.7876797,8.7876797 0 1 1 108.45498,282.88821 z"
+ sodipodi:ry="8.7876797"
+ sodipodi:rx="8.7876797"
+ sodipodi:cy="282.88821"
+ sodipodi:cx="99.667305"
+ id="path2896"
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1.17324996;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ transform="translate(-138.895,0)"
+ id="g3076">
+ <g
+ transform="translate(-8.39025,0)"
+ id="g3042">
+ <path
+ id="path3044"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:0.72750002;stroke-linecap:round;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 251.74434,230.02968 L 247.09281,226.05818 L 242.23893,230.0407 C 243.96351,230.02965 244.22246,229.99149 244.24454,230.64788 L 244.24454,233.6507 C 244.24454,234.04213 244.55966,234.35725 244.95109,234.35725 L 249.19037,234.35725 C 249.5818,234.35725 249.89692,234.04213 249.89692,233.6507 L 249.89692,230.64788"
+ sodipodi:nodetypes="ccccccccc" />
+ <rect
+ style="opacity:1;fill:#a1a1a1;fill-opacity:1;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:0.72750002;stroke-linecap:round;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect3046"
+ width="1.1260595"
+ height="2.2521191"
+ x="246.4286"
+ y="232.10513" />
+ </g>
+ <g
+ transform="matrix(0.847116,0,0,0.847116,36.3844,36.8906)"
+ id="g3048">
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1.18048px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 243.32987,241.79807 L 234.14142,241.79807"
+ id="path3050" />
+ <path
+ id="path3052"
+ d="M 239.3129,244.33722 L 235.75172,244.33722"
+ style="fill:none;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1.18048px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1.18048px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 239.3129,246.43478 L 235.75172,246.43478"
+ id="path3054" />
+ <path
+ id="path3056"
+ d="M 239.48636,248.88562 L 234.09189,248.88562 L 234.09189,243.45405"
+ style="fill:none;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1.18048px;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1"
+ sodipodi:nodetypes="ccc" />
+ <path
+ style="opacity:1;fill:#a1a1a1;fill-opacity:1;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:0.858796;stroke-linecap:square;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 241.24351,243.98638 L 241.24351,246.04888 L 240.43101,246.04888 L 241.96226,247.58013 L 243.49351,246.04888 L 242.64976,246.04888 L 242.64976,243.98638 L 241.24351,243.98638 z"
+ id="path3058" />
+ </g>
+ <path
+ id="path3060"
+ d="M 245.43681,225.79037 L 245.43681,252.3742"
+ style="fill:none;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 298.33953,225.79037 L 298.33953,252.3742"
+ id="path3062" />
+ <g
+ transform="matrix(0.842444,-0.225732,0.225732,0.842444,-7.44249,107.397)"
+ id="g3064">
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1.16912997;stroke-linecap:square;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3066"
+ sodipodi:cx="293.76904"
+ sodipodi:cy="243.16701"
+ sodipodi:rx="2.9365864"
+ sodipodi:ry="2.9365864"
+ d="M 296.70563,243.16701 A 2.9365864,2.9365864 0 1 1 290.83246,243.16701 A 2.9365864,2.9365864 0 1 1 296.70563,243.16701 z"
+ transform="matrix(0.81203,0,0,0.81203,68.3571,45.3769)" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1.14657998px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 304.01429,244.66842 L 300.87837,246.47894"
+ id="path3068" />
+ </g>
+ <path
+ d="M 304.43441,227.375 C 302.53928,227.375 301.00444,228.90984 301.00443,230.80498 C 301.00443,232.67076 302.49414,234.18843 304.34866,234.23495 L 305.6349,231.97689 L 306.17798,232.2913 L 305.77781,230.69064 L 304.14858,231.09081 L 304.72024,231.43381 L 303.54833,233.40604 C 302.71926,232.84537 302.17634,231.87806 302.17634,230.80498 C 302.17635,229.33317 303.23075,228.15568 304.60591,227.83233 L 304.83804,227.41022 C 304.59686,227.39462 304.55861,227.375 304.43441,227.375 z M 305.15315,234.23495 C 307.04829,234.23495 308.58312,232.70011 308.58312,230.80498 C 308.58312,228.93918 307.09342,227.42152 305.23891,227.375 L 303.95265,229.63306 L 303.40958,229.31865 L 303.80975,230.91931 L 305.43898,230.51915 L 304.86732,230.17615 L 306.03923,228.2039 C 306.8683,228.76457 307.41122,229.73189 307.41122,230.80498 C 307.41121,232.27677 306.35681,233.45426 304.98165,233.77762 L 304.74953,234.19973 C 304.99069,234.21533 305.02895,234.23495 305.15315,234.23495 z"
+ style="fill:#a1a1a1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.228;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3070" />
+ <path
+ id="path3072"
+ d="M 275.27017,248.65837 L 275.27017,252.84071"
+ style="fill:none;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#a1a1a1;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 275.27017,225.34016 L 275.27017,227.98729"
+ id="path3074" />
+ </g>
+ </g>
+ </g>
+</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/resource/adriankierman_cell_phone_tower.svg Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://web.resource.org/cc/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="54.71582" height="243.43661" id="svg5560" sodipodi:version="0.32" inkscape:version="0.45.1" version="1.0" sodipodi:docbase="/root/workspace/svg/clipart" sodipodi:docname="simple_cellular_tower.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs id="defs5562"/>
+ <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" gridtolerance="10000" guidetolerance="10" objecttolerance="10" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.35" inkscape:cx="375" inkscape:cy="520" inkscape:document-units="px" inkscape:current-layer="layer1" inkscape:window-width="826" inkscape:window-height="622" inkscape:window-x="5" inkscape:window-y="73"/>
+ <metadata id="metadata5565">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-241.214, -213.501)">
+ <g id="g5542" transform="translate(-505.802, 139.945)">
+ <rect y="74.055664" x="769.73621" height="242.43661" width="10.101525" id="rect4253" style="opacity: 0.535519; fill: rgb(102, 102, 102); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;"/>
+ <g transform="matrix(-1, 0, 0, 1, 1549.23, 0)" id="g5232">
+ <rect style="opacity: 0.535519; fill: rgb(0, 0, 0); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" id="rect5226" width="16.667517" height="3.0304577" x="752.56366" y="87.187645"/>
+ <rect style="opacity: 0.535519; fill: rgb(230, 230, 230); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" id="rect5228" width="3.0304713" height="26.263954" x="748.01801" y="75.570892"/>
+ </g>
+ <g transform="matrix(-1, 0, 0, 1, 1549.25, 32.9924)" id="g5236">
+ <rect style="opacity: 0.535519; fill: rgb(0, 0, 0); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" id="rect5238" width="16.667517" height="3.0304577" x="752.56366" y="87.187645"/>
+ <rect style="opacity: 0.535519; fill: rgb(230, 230, 230); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" id="rect5240" width="3.0304713" height="26.263954" x="748.01801" y="75.570892"/>
+ </g>
+ <g transform="translate(-0.48229, 0.313456)" id="g5242">
+ <rect style="opacity: 0.535519; fill: rgb(0, 0, 0); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" id="rect5244" width="16.667517" height="3.0304577" x="752.56366" y="87.187645"/>
+ <rect style="opacity: 0.535519; fill: rgb(230, 230, 230); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" id="rect5246" width="3.0304713" height="26.263954" x="748.01801" y="75.570892"/>
+ </g>
+ <g transform="translate(-0.50263, 33.3059)" id="g5248">
+ <rect style="opacity: 0.535519; fill: rgb(0, 0, 0); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" id="rect5250" width="16.667517" height="3.0304577" x="752.56366" y="87.187645"/>
+ <rect style="opacity: 0.535519; fill: rgb(230, 230, 230); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" id="rect5252" width="3.0304713" height="26.263954" x="748.01801" y="75.570892"/>
+ </g>
+ <path transform="translate(-3.01015, 3.03046)" d="M 776.80727 152.84756 A 7.0710678 12.12183 0 1 1 762.66514,152.84756 A 7.0710678 12.12183 0 1 1 776.80727 152.84756 z" sodipodi:ry="12.12183" sodipodi:rx="7.0710678" sodipodi:cy="152.84756" sodipodi:cx="769.73621" id="path5254" style="opacity: 1; fill: rgb(128, 128, 128); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" sodipodi:type="arc"/>
+ <path transform="translate(11.1117, -46.467)" d="M 776.80727 152.84756 A 7.0710678 12.12183 0 1 1 762.66514,152.84756 A 7.0710678 12.12183 0 1 1 776.80727 152.84756 z" sodipodi:ry="12.12183" sodipodi:rx="7.0710678" sodipodi:cy="152.84756" sodipodi:cx="769.73621" id="path5256" style="opacity: 1; fill: rgb(128, 128, 128); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" sodipodi:type="arc"/>
+ <path transform="translate(11.132, 3.03045)" d="M 776.80727 152.84756 A 7.0710678 12.12183 0 1 1 762.66514,152.84756 A 7.0710678 12.12183 0 1 1 776.80727 152.84756 z" sodipodi:ry="12.12183" sodipodi:rx="7.0710678" sodipodi:cy="152.84756" sodipodi:cx="769.73621" id="path5258" style="opacity: 1; fill: rgb(128, 128, 128); fill-opacity: 1; stroke: rgb(0, 0, 0); stroke-opacity: 1;" sodipodi:type="arc"/>
+ </g>
+ </g>
+</svg>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/resource/bobocal_Yellow_Bus.svg Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.0" width="993.7254" height="310.89276" id="svg2160">
+ <defs id="defs2162"/>
+ <g transform="translate(-31.7463, -366.985)" id="layer1">
+ <g id="g2386">
+ <path d="M 32.325515,677.29858 L 1024.8925,677.29858" style="fill: none; fill-rule: evenodd; stroke: rgb(0, 0, 0); stroke-width: 1.15842px; stroke-linecap: butt; stroke-linejoin: miter; stroke-opacity: 1;" id="path2175"/>
+ <path d="M 268.79664,628.66538 A 35.70126,35.70126 0 1 1 273.83521,598.26515 L 239.54393,608.19934 z" transform="translate(34.5496)" style="opacity: 0; fill: rgb(230, 230, 230); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: 2, 2; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="path2177"/>
+ <path d="M 89.0625,367.875 C 69.163607,367.875 53.15625,383.75548 53.15625,403.46875 L 53.15625,609.15625 C 53.15625,628.86953 69.163611,644.75 89.0625,644.75 L 975.40625,644.75 C 995.30514,644.75 1011.3125,628.86955 1011.3125,609.15625 L 1011.3125,403.46875 C 1011.3125,402.85271 1011.3121,402.23319 1011.2813,401.625 L 1011.2813,552 C 1011.2805,553.76879 1009.8625,555.1875 1008.0938,555.1875 L 990.90625,555.1875 C 989.13747,555.18752 987.6875,553.76879 987.6875,552 L 987.6875,399.5625 C 987.6875,397.79372 989.13746,396.375 990.90625,396.375 L 1008.0938,396.375 C 1009.3388,396.375 1010.3796,397.08915 1010.9063,398.125 C 1008.321,380.9619 993.47184,367.875 975.40625,367.875 L 89.0625,367.875 z M 846,388.46875 L 897.875,388.46875 C 898.5113,388.46875 899.11956,388.53428 899.71875,388.65625 C 900.30819,388.77624 900.8956,388.96112 901.4375,389.1875 C 901.44596,389.19107 901.4603,389.18391 901.46875,389.1875 C 902.01063,389.41591 902.51759,389.70788 903,390.03125 C 903.97724,390.69034 904.84091,391.55401 905.5,392.53125 C 905.82337,393.01366 906.11534,393.52062 906.34375,394.0625 C 906.34734,394.07095 906.34018,394.08529 906.34375,394.09375 C 906.57013,394.63565 906.75501,395.22307 906.875,395.8125 C 906.9024,395.94709 906.9159,396.08303 906.9375,396.21875 C 907.6293,391.82302 911.40379,388.46876 916,388.46875 L 967.875,388.46875 C 972.96536,388.46875 977.06251,392.56592 977.0625,397.65625 L 977.0625,616.53125 C 977.06251,621.62157 972.96535,625.71873 967.875,625.71875 L 916,625.71875 C 911.38062,625.71872 907.57016,622.36596 906.90625,617.9375 C 906.87063,618.14482 906.85874,618.35754 906.8125,618.5625 C 906.69252,619.09427 906.55109,619.63312 906.34375,620.125 C 906.23319,620.38729 906.10226,620.65719 905.96875,620.90625 C 905.96211,620.9185 905.94419,620.92528 905.9375,620.9375 C 905.7964,621.19791 905.66477,621.44319 905.5,621.6875 C 905.00568,622.42043 904.39885,623.06421 903.71875,623.625 C 902.58525,624.55966 901.21674,625.22631 899.71875,625.53125 C 899.11956,625.65323 898.51129,625.71875 897.875,625.71875 L 846,625.71875 C 845.37436,625.71875 844.74612,625.64927 844.15625,625.53125 C 844.14649,625.52926 844.13475,625.53327 844.125,625.53125 C 843.23571,625.35022 842.3994,625.04152 841.625,624.625 C 841.61734,624.62085 841.6014,624.62917 841.59375,624.625 C 841.08026,624.34677 840.60429,623.99242 840.15625,623.625 C 840.15031,623.62011 840.13092,623.62991 840.125,623.625 C 839.66523,623.24588 839.28415,622.83582 838.90625,622.375 C 838.71932,622.1483 838.53977,621.93181 838.375,621.6875 C 838.205,621.43544 838.05102,621.17541 837.90625,620.90625 C 837.76515,620.64585 837.64716,620.39998 837.53125,620.125 C 837.29555,619.56583 837.12204,618.98496 837,618.375 C 836.88198,617.78513 836.8125,617.15689 836.8125,616.53125 L 836.8125,397.65625 C 836.8125,397.01995 836.87803,396.41169 837,395.8125 C 837.06297,395.50315 837.12571,395.20327 837.21875,394.90625 C 837.30505,394.62758 837.41993,394.36021 837.53125,394.09375 C 837.53493,394.08503 837.52755,394.07121 837.53125,394.0625 C 837.64716,393.78752 837.76515,393.54165 837.90625,393.28125 C 838.05132,393.01352 838.20493,392.75069 838.375,392.5 C 838.53977,392.25569 838.71932,392.0392 838.90625,391.8125 C 839.6626,390.89524 840.56851,390.12884 841.625,389.5625 C 841.8854,389.4214 842.13127,389.30341 842.40625,389.1875 C 842.89813,388.98016 843.43698,388.83873 843.96875,388.71875 C 844.61866,388.57397 845.30518,388.46875 846,388.46875 z M 169.15625,389.03125 L 272.09375,389.03125 C 277.1841,389.03125 281.28125,393.12841 281.28125,398.21875 L 281.28125,464.53125 C 281.28124,469.62158 277.18409,473.71875 272.09375,473.71875 L 169.15625,473.71875 C 164.06591,473.71876 159.96875,469.62159 159.96875,464.53125 L 159.96875,398.21875 C 159.96875,393.12839 164.06591,389.03125 169.15625,389.03125 z M 305.3125,389.03125 L 408.28125,389.03125 C 413.37158,389.03125 417.46875,393.12841 417.46875,398.21875 L 417.46875,464.53125 C 417.46876,469.62158 413.37159,473.71875 408.28125,473.71875 L 305.3125,473.71875 C 300.22218,473.71876 296.125,469.62159 296.125,464.53125 L 296.125,398.21875 C 296.125,393.12839 300.22216,389.03125 305.3125,389.03125 z M 441.46875,389.03125 L 544.4375,389.03125 C 549.52786,389.03125 553.625,393.12841 553.625,398.21875 L 553.625,464.53125 C 553.62501,469.62158 549.52784,473.71875 544.4375,473.71875 L 441.46875,473.71875 C 436.37843,473.71876 432.28125,469.62159 432.28125,464.53125 L 432.28125,398.21875 C 432.28125,393.12839 436.37841,389.03125 441.46875,389.03125 z M 577.65625,389.03125 L 680.59375,389.03125 C 685.68408,389.03125 689.78125,393.12841 689.78125,398.21875 L 689.78125,464.53125 C 689.78123,469.62158 685.68409,473.71875 680.59375,473.71875 L 577.65625,473.71875 C 572.56589,473.71876 568.4375,469.62159 568.4375,464.53125 L 568.4375,398.21875 C 568.4375,393.12839 572.56591,389.03125 577.65625,389.03125 z M 713.8125,389.03125 L 816.78125,389.03125 C 821.87158,389.03125 825.96875,393.12841 825.96875,398.21875 L 825.96875,464.53125 C 825.96873,469.62158 821.87159,473.71875 816.78125,473.71875 L 713.8125,473.71875 C 708.72214,473.71876 704.625,469.62159 704.625,464.53125 L 704.625,398.21875 C 704.625,393.12839 708.72216,389.03125 713.8125,389.03125 z " style="fill: rgb(255, 255, 0); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77992; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect3179"/>
+ <path d="M 177.12517,635.35797 A 27.063858,27.063858 0 1 1 229.40853,635.35797" transform="matrix(2.19149, 0, 0, 2.19149, -207.641, -748.676)" style="opacity: 1; fill: rgb(77, 77, 77); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 2.00004; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="path3167"/>
+ <path d="M 230.3307 628.35333 A 27.063858 27.063858 0 1 1 176.20299,628.35333 A 27.063858 27.063858 0 1 1 230.3307 628.35333 z" transform="matrix(1.80851, 0, 0, 1.80851, -129.794, -508.03)" style="opacity: 1; fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: 2, 2; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="path3152"/>
+ <path d="M 230.3307 628.35333 A 27.063858 27.063858 0 1 1 176.20299,628.35333 A 27.063858 27.063858 0 1 1 230.3307 628.35333 z" transform="matrix(0.787234, 0, 0, 0.787234, 77.7979, 133.692)" style="opacity: 1; fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: 2, 2; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="path3156"/>
+ <path d="M 268.79664,628.66538 A 35.70126,35.70126 0 1 1 273.83521,598.26515 L 239.54393,608.19934 z" transform="translate(560.207)" style="opacity: 0; fill: rgb(230, 230, 230); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: 2, 2; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="path3169"/>
+ <path d="M 177.12517,635.35797 A 27.063858,27.063858 0 1 1 229.40853,635.35797" transform="matrix(2.19149, 0, 0, 2.19149, 318.017, -748.676)" style="opacity: 1; fill: rgb(77, 77, 77); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 2.00004; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="path3171"/>
+ <path d="M 230.3307 628.35333 A 27.063858 27.063858 0 1 1 176.20299,628.35333 A 27.063858 27.063858 0 1 1 230.3307 628.35333 z" transform="matrix(1.80851, 0, 0, 1.80851, 395.864, -508.03)" style="opacity: 1; fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: 2, 2; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="path3173"/>
+ <path d="M 230.3307 628.35333 A 27.063858 27.063858 0 1 1 176.20299,628.35333 A 27.063858 27.063858 0 1 1 230.3307 628.35333 z" transform="matrix(0.787234, 0, 0, 0.787234, 603.456, 133.692)" style="opacity: 1; fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: 2, 2; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="path3175"/>
+ <rect width="121.33695" height="84.691566" rx="9.1883392" ry="9.1883392" x="159.95827" y="389.04138" style="opacity: 0.6; fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect3181"/>
+ <rect width="23.578625" height="158.81502" rx="3.1927617" ry="3.1927617" x="987.69922" y="396.37573" style="opacity: 0.6; fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.00874; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect4170"/>
+ <rect width="40.307938" height="41.459484" rx="4.8945279" ry="5.6405392" x="980.63293" y="608.1994" style="opacity: 1; fill: rgb(40, 11, 11); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect4176"/>
+ <rect width="114.01376" height="41.459484" rx="4.8945279" ry="5.6405392" x="47.217747" y="608.1994" style="opacity: 1; fill: rgb(40, 11, 11); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect4178"/>
+ <rect width="161.23149" height="78.312439" rx="7.9438462" ry="7.9438462" x="499.61023" y="560.98157" style="opacity: 1; fill: none; fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect4184"/>
+ <rect width="161.23149" height="78.312439" rx="7.9438462" ry="7.9438462" x="330.31717" y="560.98157" style="opacity: 1; fill: none; fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect4186"/>
+ <rect width="121.33695" height="84.691566" rx="9.1883392" ry="9.1883392" x="296.12302" y="389.04138" style="opacity: 0.6; fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect2197"/>
+ <rect width="121.33695" height="84.691566" rx="9.1883392" ry="9.1883392" x="432.28775" y="389.04138" style="opacity: 0.6; fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect2199"/>
+ <rect width="121.33695" height="84.691566" rx="9.1883392" ry="9.1883392" x="568.45251" y="389.04138" style="opacity: 0.6; fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect2201"/>
+ <rect width="121.33695" height="84.691566" rx="9.1883392" ry="9.1883392" x="704.61719" y="388.48038" style="opacity: 0.6; fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect2203"/>
+ <g transform="translate(-0.000273234, 0.000265655)" id="g2378">
+ <g id="g2370">
+ <path d="M 915.98701,388.48039 C 910.89667,388.48039 906.79951,392.57753 906.79951,397.66789 L 906.79951,616.54289 C 906.79951,621.63323 910.89665,625.73036 915.98701,625.73039 L 967.86201,625.73039 C 972.95235,625.73039 977.04952,621.63321 977.04951,616.54289 L 977.04951,397.66789 C 977.04951,392.57755 972.95237,388.48039 967.86201,388.48039 L 915.98701,388.48039 z M 924.04951,397.69914 L 960.95576,397.69914 C 966.04613,397.69914 970.14326,401.7963 970.14326,406.88664 L 970.14326,519.79289 C 970.14327,524.8832 966.0461,528.98039 960.95576,528.98039 L 924.04951,528.98039 C 918.95915,528.98041 914.86201,524.88323 914.86201,519.79289 L 914.86201,406.88664 C 914.86201,401.79632 918.95917,397.69914 924.04951,397.69914 z " style="fill: rgb(255, 255, 0); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="path2351"/>
+ <rect width="55.279369" height="131.2885" rx="9.1883392" ry="9.1883392" x="914.87238" y="397.70074" style="opacity: 0.6; fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect2353"/>
+ </g>
+ <g id="g2374">
+ <path d="M 845.9886,388.48039 C 840.89826,388.48039 836.8011,392.57753 836.8011,397.66789 L 836.8011,616.54289 C 836.8011,621.63323 840.89824,625.73036 845.9886,625.73039 L 897.8636,625.73039 C 902.95394,625.73039 907.05111,621.63321 907.0511,616.54289 L 907.0511,397.66789 C 907.0511,392.57755 902.95396,388.48039 897.8636,388.48039 L 845.9886,388.48039 z M 854.0511,397.69914 L 890.95735,397.69914 C 896.04772,397.69914 900.14485,401.7963 900.14485,406.88664 L 900.14485,519.79289 C 900.14486,524.8832 896.04769,528.98039 890.95735,528.98039 L 854.0511,528.98039 C 848.96074,528.98041 844.8636,524.88323 844.8636,519.79289 L 844.8636,406.88664 C 844.8636,401.79632 848.96076,397.69914 854.0511,397.69914 z " style="fill: rgb(255, 255, 0); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="path2284"/>
+ <rect width="55.279369" height="131.2885" rx="9.1883392" ry="9.1883392" x="844.87396" y="397.70074" style="opacity: 0.6; fill: rgb(204, 204, 204); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(0, 0, 0); stroke-width: 1.77165; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1;" id="rect2286"/>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/resource/thilakarathna_Bus_Halt.svg Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://web.resource.org/cc/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="178.37129" height="264.19806" id="svg6213" sodipodi:version="0.32" inkscape:version="0.45" version="1.0" sodipodi:docbase="/home/fahad/Desktop" sodipodi:docname="thilakarathna_Bus_Halt.svg" inkscape:export-filename="/home/fahad/Desktop/Bus_Halt.png" inkscape:export-xdpi="150" inkscape:export-ydpi="150" inkscape:output_extension="org.inkscape.output.svg.inkscape" sodipodi:modified="TRUE">
+ <defs id="defs6215">
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient5498" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.83731, 0, 0, 0.83731, 122.117, -209.238)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" id="linearGradient5456">
+ <stop style="stop-color: rgb(0, 255, 255); stop-opacity: 1;" offset="0" id="stop5458"/>
+ <stop style="stop-color: rgb(0, 255, 255); stop-opacity: 0;" offset="1" id="stop5460"/>
+ </linearGradient>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient5496" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.508315, 0, 0, 0.508315, 196.577, -737.854)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient6269" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.135991, 0, 0, 0.135991, 381.515, 500.095)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient6282" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.0825575, 0, 0, 0.0825575, 393.609, 414.241)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient6312" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.0825575, 0, 0, 0.0825575, 393.609, 414.241)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient6314" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.135991, 0, 0, 0.135991, 381.515, 500.095)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient6331" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.135991, 0, 0, 0.135991, 381.515, 500.095)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient6344" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.0825575, 0, 0, 0.0825575, 393.609, 414.241)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient6373" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.0825575, 0, 0, 0.0825575, 393.609, 414.241)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient6375" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.135991, 0, 0, 0.135991, 381.515, 500.095)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient1928" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.135991, 0, 0, 0.135991, 381.515, 500.095)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient1941" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.0825575, 0, 0, 0.0825575, 393.609, 414.241)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient2296" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.135991, 0, 0, 0.135991, 381.515, 500.095)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient5456" id="linearGradient2312" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.0825575, 0, 0, 0.0825575, 393.609, 414.241)" x1="527.5" y1="-1917.553" x2="479.5" y2="-131.72624"/>
+ </defs>
+ <sodipodi:namedview inkscape:document-units="mm" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.86831672" inkscape:cx="303.71381" inkscape:cy="154.70921" inkscape:current-layer="layer1" id="namedview6217" inkscape:window-width="872" inkscape:window-height="622" inkscape:window-x="25" inkscape:window-y="49"/>
+ <metadata id="metadata6219">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title>Bus Halt</dc:title>
+ <dc:date>07 th June 2008</dc:date>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Kenneth Thilakarathna - UCSC - LK</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <cc:license rdf:resource="http://web.resource.org/cc/PublicDomain"/>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>bus-stop</rdf:li>
+ <rdf:li>bus-halt</rdf:li>
+ <rdf:li>bus stop</rdf:li>
+ <rdf:li>bus halt</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </cc:Work>
+ <cc:License rdf:about="http://web.resource.org/cc/PublicDomain">
+ <cc:permits rdf:resource="http://web.resource.org/cc/Reproduction"/>
+ <cc:permits rdf:resource="http://web.resource.org/cc/Distribution"/>
+ <cc:permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/>
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-323.107, -221.199)">
+ <g id="g2314">
+ <rect y="305.73587" x="399.18127" height="80.035286" width="59.441422" id="rect5464" style="fill: url(#linearGradient2312) rgb(0, 0, 0); fill-opacity: 1; stroke: rgb(7, 0, 0); stroke-width: 0.87758; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;"/>
+ <g transform="matrix(0.102737, 0, 0, 0.102737, 376.38, 502.333)" id="g5414">
+ <path sodipodi:type="arc" style="fill: rgb(10, 0, 0); fill-opacity: 1; stroke: rgb(7, 0, 0); stroke-width: 4.53543; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;" id="path5416" sodipodi:cx="600" sodipodi:cy="-992.12592" sodipodi:rx="168" sodipodi:ry="38.267715" d="M 768 -992.12592 A 168 38.267715 0 1 1 432,-992.12592 A 168 38.267715 0 1 1 768 -992.12592 z" transform="translate(-0.5, 5.75398)"/>
+ <path style="fill: none; fill-rule: evenodd; stroke: rgb(0, 0, 0); stroke-width: 86.0315; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;" d="M 595.50002,-946.1042 C 595.50002,-742.00971 595.50002,-742.00971 595.50002,-742.00971" id="path5418"/>
+ </g>
+ <g transform="matrix(0.0922068, 0, 0, 0.0922068, 376.384, 471.043)" id="g5420">
+ <path sodipodi:type="arc" style="fill: rgb(10, 0, 0); fill-opacity: 1; stroke: rgb(7, 0, 0); stroke-width: 4.53543; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;" id="path5422" sodipodi:cx="600" sodipodi:cy="-992.12592" sodipodi:rx="168" sodipodi:ry="38.267715" d="M 768 -992.12592 A 168 38.267715 0 1 1 432,-992.12592 A 168 38.267715 0 1 1 768 -992.12592 z" transform="translate(-0.5, 5.75398)"/>
+ <path style="fill: none; fill-rule: evenodd; stroke: rgb(0, 0, 0); stroke-width: 86.0315; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;" d="M 595.50002,-946.1042 C 595.50002,-742.00971 595.50002,-742.00971 595.50002,-742.00971" id="path5424"/>
+ </g>
+ <g transform="matrix(0.141346, 0, 0, 0.141346, 360.279, 565.272)" id="g5394">
+ <path sodipodi:type="arc" style="fill: rgb(10, 0, 0); fill-opacity: 1; stroke: rgb(7, 0, 0); stroke-width: 4.53543; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;" id="path5390" sodipodi:cx="600" sodipodi:cy="-992.12592" sodipodi:rx="168" sodipodi:ry="38.267715" d="M 768 -992.12592 A 168 38.267715 0 1 1 432,-992.12592 A 168 38.267715 0 1 1 768 -992.12592 z" transform="translate(-0.5, 5.75398)"/>
+ <path style="fill: none; fill-rule: evenodd; stroke: rgb(0, 0, 0); stroke-width: 86.0315; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;" d="M 595.50002,-946.1042 C 595.50002,-742.00971 595.50002,-742.00971 595.50002,-742.00971" id="path5392"/>
+ </g>
+ <path sodipodi:end="6.2814937" sodipodi:start="3.1458674" transform="matrix(0.139174, 0, 0, 0.125355, 348.828, 471.175)" d="M -143.99452,-1250.2431 A 600,701.57483 0 0 1 1055.9991,-1248.4308 L 456,-1247.244 z" sodipodi:ry="701.57483" sodipodi:rx="600" sodipodi:cy="-1247.244" sodipodi:cx="456" id="path5398" style="fill: rgb(85, 34, 0); fill-opacity: 1; stroke: rgb(7, 0, 0); stroke-width: 86.0315; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;" sodipodi:type="arc"/>
+ <path id="path5402" d="M 495.51107,320.20041 C 495.51107,479.43124 495.51107,479.43124 495.51107,479.43124" style="fill: none; fill-rule: evenodd; stroke: rgb(0, 0, 0); stroke-width: 11.9325; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;"/>
+ <path id="path5410" d="M 478.53868,320.4603 C 478.53868,448.7526 478.53868,448.7526 478.53868,448.7526" style="fill: none; fill-rule: evenodd; stroke: rgb(0, 0, 0); stroke-width: 10.7107; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;"/>
+ <path id="path5412" d="M 462.94693,320.38103 C 462.94693,400.73806 462.94693,400.73806 462.94693,400.73806" style="fill: none; fill-rule: evenodd; stroke: rgb(0, 0, 0); stroke-width: 8.47674; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;"/>
+ <rect y="321.36346" x="390.69467" height="131.83618" width="97.913445" id="rect5454" style="fill: url(#linearGradient2296) rgb(0, 0, 0); fill-opacity: 1; stroke: rgb(7, 0, 0); stroke-width: 1.44557; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;"/>
+ <g transform="matrix(0.162414, 0, 0, 0.162414, 396.455, 615.534)" id="g5378">
+ <g id="g5366" transform="translate(-556.499, -494.074)">
+ <g transform="matrix(0.996807, 0.0798454, -0.0798454, 0.996807, -118.094, -58.9951)" id="g5360">
+ <g id="g5341" transform="matrix(0.999743, -0.022669, 0.022669, 0.999743, 34.3179, 14.9971)">
+ <path transform="matrix(0.998587, 0.0531485, -0.0531485, 0.998587, -560.394, 98.4307)" d="M 1248,-1479.685 L 1086.5899,-1467.0142 L 963.49616,-1572.1888 L 950.8254,-1733.5989 L 1056,-1856.6927 L 1217.4101,-1869.3634 L 1340.5038,-1764.1888 L 1353.1746,-1602.7788 L 1248,-1479.685 z " inkscape:randomized="0" inkscape:rounded="0" inkscape:flatsided="true" sodipodi:arg2="1.4924566" sodipodi:arg1="1.0997575" sodipodi:r2="171.14047" sodipodi:r1="211.54124" sodipodi:cy="-1668.1888" sodipodi:cx="1152" sodipodi:sides="8" id="path4558" style="fill: rgb(255, 0, 0);" sodipodi:type="star"/>
+ <g transform="matrix(0.998587, 0.0531485, -0.0531485, 0.998587, -79.0917, -38.1975)" id="g5338">
+ <path sodipodi:type="star" style="fill: rgb(255, 255, 255);" id="path4566" sodipodi:sides="8" sodipodi:cx="1152" sodipodi:cy="-1668.1888" sodipodi:r1="211.54124" sodipodi:r2="171.14047" sodipodi:arg1="1.0997575" sodipodi:arg2="1.4924566" inkscape:flatsided="true" inkscape:rounded="0" inkscape:randomized="0" d="M 1248,-1479.685 L 1086.5899,-1467.0142 L 963.49616,-1572.1888 L 950.8254,-1733.5989 L 1056,-1856.6927 L 1217.4101,-1869.3634 L 1340.5038,-1764.1888 L 1353.1746,-1602.7788 L 1248,-1479.685 z " transform="matrix(0.928634, 0, 0, 0.928634, -391.436, 43.3049)"/>
+ </g>
+ </g>
+ </g>
+ <path id="text5346" d="M 607.79673,-1572.4732 C 610.28949,-1572.4731 612.17962,-1573.021 613.46715,-1574.1167 C 614.75459,-1575.2124 615.39833,-1576.8286 615.39837,-1578.9654 C 615.39833,-1581.0746 614.75459,-1582.6771 613.46715,-1583.7729 C 612.17962,-1584.896 610.28949,-1585.4575 607.79673,-1585.4576 L 599.04458,-1585.4576 L 599.04458,-1572.4732 L 607.79673,-1572.4732 M 608.3309,-1545.6414 C 611.50849,-1545.6414 613.8917,-1546.3125 615.48055,-1547.6548 C 617.09672,-1548.9971 617.90482,-1551.0242 617.90486,-1553.7361 C 617.90482,-1556.3933 617.11041,-1558.3793 615.52164,-1559.6942 C 613.93279,-1561.0364 611.53588,-1561.7076 608.3309,-1561.7076 L 599.04458,-1561.7076 L 599.04458,-1545.6414 L 608.3309,-1545.6414 M 623.0411,-1567.7067 C 626.43782,-1566.7205 629.06757,-1564.8989 630.93037,-1562.2418 C 632.79306,-1559.5846 633.72443,-1556.3248 633.72449,-1552.4623 C 633.72443,-1546.5454 631.72472,-1542.1351 627.72536,-1539.2314 C 623.72589,-1536.3277 617.64458,-1534.8759 609.48142,-1534.8759 L 583.22495,-1534.8759 L 583.22495,-1596.2231 L 606.97494,-1596.2231 C 615.49421,-1596.2231 621.65769,-1594.9356 625.46541,-1592.3607 C 629.30042,-1589.7857 631.21794,-1585.663 631.218,-1579.9926 C 631.21794,-1577.0067 630.51942,-1574.4591 629.12241,-1572.3499 C 627.7253,-1570.268 625.6982,-1568.7202 623.0411,-1567.7067 M 647.32526,-1596.2231 L 663.14489,-1596.2231 L 663.14489,-1559.4476 C 663.14486,-1554.3799 663.96666,-1550.7503 665.61028,-1548.5588 C 667.28125,-1546.3947 669.99318,-1545.3127 673.74609,-1545.3127 C 677.52633,-1545.3127 680.23826,-1546.3947 681.8819,-1548.5588 C 683.55285,-1550.7503 684.38834,-1554.3799 684.38839,-1559.4476 L 684.38839,-1596.2231 L 700.20801,-1596.2231 L 700.20801,-1559.4476 C 700.20795,-1550.7639 698.03019,-1544.2991 693.67471,-1540.0532 C 689.31913,-1535.8072 682.67626,-1533.6842 673.74609,-1533.6842 C 664.84325,-1533.6842 658.21408,-1535.8072 653.85856,-1540.0532 C 649.50302,-1544.2991 647.32525,-1550.7639 647.32526,-1559.4476 L 647.32526,-1596.2231 M 758.39137,-1594.2919 L 758.39137,-1581.3075 C 755.02194,-1582.8141 751.73475,-1583.9509 748.52978,-1584.718 C 745.32473,-1585.4849 742.29777,-1585.8684 739.4489,-1585.8685 C 735.6686,-1585.8684 732.87449,-1585.3479 731.06656,-1584.3071 C 729.25858,-1583.2661 728.3546,-1581.6499 728.35462,-1579.4584 C 728.3546,-1577.8148 728.95725,-1576.5273 730.16258,-1575.596 C 731.39525,-1574.692 733.61411,-1573.9113 736.81915,-1573.2539 L 743.5579,-1571.8979 C 750.37879,-1570.5282 755.22739,-1568.4463 758.10374,-1565.6522 C 760.97998,-1562.8581 762.41813,-1558.8861 762.41818,-1553.7361 C 762.41813,-1546.97 760.40472,-1541.9296 756.37796,-1538.615 C 752.37849,-1535.3278 746.2561,-1533.6842 738.01076,-1533.6842 C 734.12088,-1533.6842 730.21734,-1534.0541 726.30012,-1534.7937 C 722.38287,-1535.5333 718.46563,-1536.629 714.5484,-1538.0809 L 714.5484,-1551.4351 C 718.46563,-1549.3532 722.2459,-1547.7781 725.88922,-1546.7098 C 729.5599,-1545.6688 733.09363,-1545.1483 736.49043,-1545.1483 C 739.94195,-1545.1483 742.5854,-1545.7236 744.42079,-1546.8741 C 746.2561,-1548.0246 747.17377,-1549.6682 747.17381,-1551.8049 C 747.17377,-1553.7224 746.54373,-1555.2017 745.28368,-1556.2426 C 744.05094,-1557.2835 741.57185,-1558.2149 737.8464,-1559.0367 L 731.72399,-1560.3927 C 725.58788,-1561.7076 721.09538,-1563.8031 718.24649,-1566.6795 C 715.42498,-1569.5557 714.01422,-1573.4319 714.01423,-1578.3079 C 714.01422,-1584.4166 715.98654,-1589.1145 719.93118,-1592.4018 C 723.8758,-1595.6889 729.54621,-1597.3325 736.94242,-1597.3326 C 740.31176,-1597.3325 743.77701,-1597.0723 747.33817,-1596.5519 C 750.89926,-1596.0587 754.58365,-1595.3054 758.39137,-1594.2919 M 596.9079,-1489.1017 L 596.9079,-1476.1172 C 593.53848,-1477.6238 590.25129,-1478.7606 587.04631,-1479.5277 C 583.84126,-1480.2947 580.81431,-1480.6782 577.96544,-1480.6782 C 574.18513,-1480.6782 571.39102,-1480.1577 569.58309,-1479.1168 C 567.77511,-1478.0758 566.87113,-1476.4596 566.87115,-1474.2682 C 566.87113,-1472.6246 567.47378,-1471.3371 568.67911,-1470.4057 C 569.91179,-1469.5017 572.13064,-1468.721 575.33568,-1468.0636 L 582.07443,-1466.7076 C 588.89532,-1465.3379 593.74393,-1463.2561 596.62027,-1460.462 C 599.49651,-1457.6678 600.93466,-1453.6958 600.93471,-1448.5459 C 600.93466,-1441.7797 598.92125,-1436.7394 594.89449,-1433.4248 C 590.89503,-1430.1376 584.77263,-1428.494 576.52729,-1428.494 C 572.63742,-1428.494 568.73387,-1428.8638 564.81666,-1429.6034 C 560.8994,-1430.343 556.98216,-1431.4388 553.06493,-1432.8906 L 553.06493,-1446.2449 C 556.98216,-1444.1629 560.76243,-1442.5878 564.40576,-1441.5195 C 568.07644,-1440.4786 571.61017,-1439.9581 575.00696,-1439.9581 C 578.45848,-1439.9581 581.10193,-1440.5333 582.93732,-1441.6839 C 584.77263,-1442.8344 585.69031,-1444.478 585.69035,-1446.6147 C 585.69031,-1448.5322 585.06026,-1450.0114 583.80021,-1451.0524 C 582.56747,-1452.0933 580.08838,-1453.0247 576.36293,-1453.8465 L 570.24053,-1455.2025 C 564.10441,-1456.5173 559.61192,-1458.6129 556.76303,-1461.4892 C 553.94151,-1464.3655 552.53076,-1468.2416 552.53076,-1473.1177 C 552.53076,-1479.2263 554.50307,-1483.9243 558.44772,-1487.2115 C 562.39233,-1490.4987 568.06274,-1492.1423 575.45895,-1492.1423 C 578.82829,-1492.1423 582.29354,-1491.882 585.85471,-1491.3616 C 589.41579,-1490.8685 593.10019,-1490.1152 596.9079,-1489.1017 M 607.5502,-1491.0329 L 664.08995,-1491.0329 L 664.08995,-1479.0757 L 643.75043,-1479.0757 L 643.75043,-1429.6856 L 627.93081,-1429.6856 L 627.93081,-1479.0757 L 607.5502,-1479.0757 L 607.5502,-1491.0329 M 700.24911,-1480.6782 C 695.42786,-1480.6782 691.68867,-1478.8976 689.03155,-1475.3365 C 686.37438,-1471.7754 685.04581,-1466.7624 685.04583,-1460.2976 C 685.04581,-1453.8602 686.37438,-1448.8609 689.03155,-1445.2998 C 691.68867,-1441.7386 695.42786,-1439.9581 700.24911,-1439.9581 C 705.09768,-1439.9581 708.85056,-1441.7386 711.50775,-1445.2998 C 714.16485,-1448.8609 715.49342,-1453.8602 715.49347,-1460.2976 C 715.49342,-1466.7624 714.16485,-1471.7754 711.50775,-1475.3365 C 708.85056,-1478.8976 705.09768,-1480.6782 700.24911,-1480.6782 M 700.24911,-1492.1423 C 710.11065,-1492.1423 717.83555,-1489.3207 723.42383,-1483.6778 C 729.012,-1478.0347 731.80611,-1470.2413 731.80618,-1460.2976 C 731.80611,-1450.3812 729.012,-1442.6015 723.42383,-1436.9585 C 717.83555,-1431.3155 710.11065,-1428.494 700.24911,-1428.494 C 690.41489,-1428.494 682.68999,-1431.3155 677.07438,-1436.9585 C 671.48614,-1442.6015 668.69203,-1450.3812 668.69203,-1460.2976 C 668.69203,-1470.2413 671.48614,-1478.0347 677.07438,-1483.6778 C 682.68999,-1489.3207 690.41489,-1492.1423 700.24911,-1492.1423 M 743.72226,-1491.0329 L 769.97873,-1491.0329 C 777.78578,-1491.0328 783.7712,-1489.2934 787.93503,-1485.8145 C 792.12615,-1482.3629 794.22174,-1477.4321 794.22179,-1471.0221 C 794.22174,-1464.5846 792.12615,-1459.6264 787.93503,-1456.1475 C 783.7712,-1452.6959 777.78578,-1450.9702 769.97873,-1450.9702 L 759.54188,-1450.9702 L 759.54188,-1429.6856 L 743.72226,-1429.6856 L 743.72226,-1491.0329 M 759.54188,-1479.5688 L 759.54188,-1462.4343 L 768.29404,-1462.4343 C 771.36205,-1462.4343 773.73157,-1463.1739 775.4026,-1464.6531 C 777.07355,-1466.1597 777.90905,-1468.2827 777.90909,-1471.0221 C 777.90905,-1473.7614 777.07355,-1475.8707 775.4026,-1477.3499 C 773.73157,-1478.8291 771.36205,-1479.5687 768.29404,-1479.5688 L 759.54188,-1479.5688" style="font-size: 84.1522px; font-style: normal; font-weight: bold; fill: rgb(255, 0, 0); fill-opacity: 1; stroke: none; stroke-width: 1px; stroke-linecap: butt; stroke-linejoin: miter; stroke-opacity: 1; font-family: Bitstream Vera Sans;"/>
+ </g>
+ <path style="fill: none; fill-opacity: 1; fill-rule: evenodd; stroke: rgb(252, 0, 0); stroke-width: 25.7953; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;" d="M 115.5,-1811.5058 C 115.5,-842.05694 115.5,-842.05694 115.5,-842.05694" id="path5376"/>
+ </g>
+ </g>
+ </g>
+</svg>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/visualizer/svgitem.py Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,156 @@
+import gobject
+import rsvg
+#import cairo
+import goocanvas
+import os.path
+
+
+class SvgItem(goocanvas.ItemSimple):
+ # setup our custom properties
+ __gproperties__ = {
+ 'x': (float, # property type
+ 'X', # property nick name
+ 'The x coordinate of a SVG image', # property description
+ -10e6, # property minimum value
+ 10e6, # property maximum value
+ 0, # property default value
+ gobject.PARAM_READWRITE), # property flags
+
+ 'y': (float,
+ 'Y',
+ 'The y coordinate of a SVG image',
+ -10e6,
+ 10e6,
+ 0,
+ gobject.PARAM_READWRITE),
+
+ 'width': (float,
+ 'Width',
+ 'The width of the SVG Image',
+ 0,
+ 10e6,
+ 0,
+ gobject.PARAM_READWRITE),
+
+ 'height': (float,
+ 'Height',
+ 'The width of the SVG Image',
+ 0,
+ 10e6,
+ 0,
+ gobject.PARAM_READWRITE),
+ }
+
+ def __init__(self, x, y, rsvg_handle, **kwargs):
+ super(SvgItem, self).__init__(**kwargs)
+ assert isinstance(rsvg_handle, rsvg.Handle)
+ self.x = x
+ self.y = y
+ self.sx = 1.0
+ self.sy = 1.0
+ self.handle = rsvg_handle
+ self.width = self.handle.props.width
+ self.height = self.handle.props.height
+ self.custom_width = None
+ self.custom_height = None
+
+ def do_set_property(self, pspec, value):
+ if pspec.name == 'x':
+ self.x = value
+
+ # make sure we update the display
+ self.changed(True)
+
+ elif pspec.name == 'y':
+ self.y = value
+
+ # make sure we update the display
+ self.changed(True)
+
+ elif pspec.name == 'width':
+ self.custom_width = value
+ self._size_changed()
+
+ # make sure we update the display
+ self.changed(True)
+
+ elif pspec.name == 'height':
+ self.custom_height = value
+ self._size_changed()
+
+ # make sure we update the display
+ self.changed(True)
+
+ else:
+ raise AttributeError, 'unknown property %s' % pspec.name
+
+ def _size_changed(self):
+ if self.custom_width is None and self.custom_height is None:
+ self.width = self.handle.props.width
+ self.height = self.handle.props.height
+ self.sx = 1.0
+ self.sy = 1.0
+ elif self.custom_width is not None and self.custom_height is None:
+ self.width = self.custom_width
+ self.sx = self.custom_width / self.handle.props.width
+ self.sy = self.sx
+ self.height = self.handle.props.height*self.sy
+ elif self.custom_width is None and self.custom_height is not None:
+ self.height = self.custom_height
+ self.sy = self.custom_height / self.handle.props.height
+ self.sx = self.sy
+ self.width = self.handle.props.width*self.sx
+ else:
+ self.width = self.custom_width
+ self.height = self.custom_height
+ self.sx = self.custom_width / self.handle.props.width
+ self.sy = self.custom_height / self.handle.props.height
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'x':
+ return self.x
+
+ elif pspec.name == 'y':
+ return self.y
+
+ elif pspec.name == 'width':
+ self.width = self.handle.props.width
+ self.height = self.handle.props.height
+
+ return self.width
+
+ elif pspec.name == 'height':
+ return self.height
+
+ else:
+ raise AttributeError, 'unknown property %s' % pspec.name
+
+ def do_simple_paint(self, cr, bounds):
+ cr.translate(self.x, self.y)
+ cr.scale(self.sx, self.sy)
+ self.handle.render_cairo(cr)
+
+ def do_simple_update(self, cr):
+ self.bounds_x1 = float(self.x)
+ self.bounds_y1 = float(self.y)
+ self.bounds_x2 = float(self.x + self.width)
+ self.bounds_y2 = float(self.y + self.height)
+
+ def do_simple_is_item_at(self, x, y, cr, is_pointer_event):
+ if ((x < self.x) or (x > self.x + self.width)) or ((y < self.y) or (y > self.y + self.height)):
+ return False
+ else:
+ return True
+
+
+_rsvg_cache = dict()
+
+def rsvg_handle_factory(base_file_name):
+ try:
+ return _rsvg_cache[base_file_name]
+ except KeyError:
+ full_path = os.path.join(os.path.dirname(__file__), 'resource', base_file_name)
+ rsvg_handle = rsvg.Handle(full_path)
+ _rsvg_cache[base_file_name] = rsvg_handle
+ return rsvg_handle
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/visualizer/wscript Mon Dec 06 17:26:06 2010 +0000
@@ -0,0 +1,22 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+def build(bld):
+ headers = bld.new_task_gen('ns3header')
+ headers.module = 'visualizer'
+ headers.source = [
+ ]
+
+ module = bld.create_ns3_module('visualizer', ['core'])
+ module.features.append('pyembed')
+ module.env.append_value('CXXFLAGS', module.env['shlib_CXXFLAGS'])
+ module.includes = '.'
+
+ if bld.env['ENABLE_PYTHON_BINDINGS']:
+ module.source = [
+ 'model/pyviz.cc',
+ 'model/visual-simulator-impl.cc',
+ ]
+ headers.source.append('model/pyviz.h')
+ else:
+ module.source = [
+ ]
--- a/src/wscript Thu Dec 02 07:20:34 2010 -0800
+++ b/src/wscript Mon Dec 06 17:26:06 2010 +0000
@@ -62,6 +62,7 @@
'mpi',
'contrib/topology-read',
'contrib/energy',
+ 'tools/visualizer',
)
def set_options(opt):
--- a/wscript Thu Dec 02 07:20:34 2010 -0800
+++ b/wscript Mon Dec 06 17:26:06 2010 +0000
@@ -124,6 +124,9 @@
help=('Run a locally built program; argument can be a program name,'
' or a command starting with the program name.'),
type="string", default='', dest='run')
+ opt.add_option('--visualize',
+ help=('Modify --run arguments to enable the visualizer'),
+ action="store_true", default=False, dest='visualize')
opt.add_option('--command-template',
help=('Template of the command used to run the program given by --run;'
' It should be a shell command string containing %s inside,'
@@ -639,11 +642,13 @@
lcov_report()
if Options.options.run:
- wutils.run_program(Options.options.run, env, wutils.get_command_template(env))
+ wutils.run_program(Options.options.run, env, wutils.get_command_template(env),
+ visualize=Options.options.visualize)
raise SystemExit(0)
if Options.options.pyrun:
- wutils.run_python_program(Options.options.pyrun, env)
+ wutils.run_python_program(Options.options.pyrun, env,
+ visualize=Options.options.visualize)
raise SystemExit(0)
if Options.options.shell:
--- a/wutils.py Thu Dec 02 07:20:34 2010 -0800
+++ b/wutils.py Mon Dec 06 17:26:06 2010 +0000
@@ -100,10 +100,11 @@
proc_env[pathvar] = os.pathsep.join(list(env['NS3_MODULE_PATH']))
pymoddir = bld.path.find_dir('bindings/python').abspath(env)
+ pyvizdir = bld.path.find_dir('src/tools/visualizer').abspath()
if 'PYTHONPATH' in proc_env:
- proc_env['PYTHONPATH'] = os.pathsep.join([pymoddir] + [proc_env['PYTHONPATH']])
+ proc_env['PYTHONPATH'] = os.pathsep.join([pymoddir, pyvizdir] + [proc_env['PYTHONPATH']])
else:
- proc_env['PYTHONPATH'] = pymoddir
+ proc_env['PYTHONPATH'] = os.pathsep.join([pymoddir, pyvizdir])
if 'PATH' in proc_env:
proc_env['PATH'] = os.pathsep.join(list(env['NS3_EXECUTABLE_PATH']) + [proc_env['PATH']])
@@ -201,7 +202,7 @@
#print "%r ==shlex.split==> %r" % (command_template % (program_node.abspath(env),), execvec)
return program_name, execvec
-def run_program(program_string, env, command_template=None, cwd=None):
+def run_program(program_string, env, command_template=None, cwd=None, visualize=False):
"""
if command_template is not None, then program_string == program
name and argv is given by command_template with %s replaced by the
@@ -214,17 +215,21 @@
cwd = Options.options.cwd_launch
else:
cwd = Options.cwd_launch
+ if visualize:
+ execvec.append("--SimulatorImplementationType=ns3::VisualSimulatorImpl")
return run_argv(execvec, env, cwd=cwd)
-def run_python_program(program_string, env):
+def run_python_program(program_string, env, visualize=False):
env = bld.env
execvec = shlex.split(program_string)
if (Options.options.cwd_launch):
cwd = Options.options.cwd_launch
else:
cwd = Options.cwd_launch
+ if visualize:
+ execvec.append("--SimulatorImplementationType=ns3::VisualSimulatorImpl")
return run_argv([env['PYTHON']] + execvec, env, cwd=cwd)