Codereview Issue 2692041: Add pyviz module

This commit is contained in:
Gustavo J. A. M. Carneiro
2010-12-06 17:26:06 +00:00
parent 985a647077
commit de0af97b3a
28 changed files with 5474 additions and 7 deletions

View File

@@ -607,7 +607,9 @@ EXCLUDE = src/routing/olsr/olsr-state.h \
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

View File

@@ -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

View File

@@ -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).

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -0,0 +1,3 @@
from core import start, register_plugin, set_bounds, add_initialization_hook

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -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>

After

Width:  |  Height:  |  Size: 4.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -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

View File

@@ -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 = [
]

View File

@@ -62,6 +62,7 @@ all_modules = (
'mpi',
'contrib/topology-read',
'contrib/energy',
'tools/visualizer',
)
def set_options(opt):

View File

@@ -124,6 +124,9 @@ def set_options(opt):
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 @@ def shutdown(ctx):
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:

View File

@@ -100,10 +100,11 @@ def get_proc_env(os_env=None):
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 @@ def get_run_program(program_string, command_template=None):
#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 @@ def run_program(program_string, env, command_template=None, cwd=None):
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)