Codereview Issue 2692041: Add pyviz module
This commit is contained in:
@@ -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
|
||||
|
||||
9
src/tools/visualizer/doc/readme.txt
Normal file
9
src/tools/visualizer/doc/readme.txt
Normal 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
|
||||
3
src/tools/visualizer/examples/readme.txt
Normal file
3
src/tools/visualizer/examples/readme.txt
Normal 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).
|
||||
1406
src/tools/visualizer/model/pyviz.cc
Normal file
1406
src/tools/visualizer/model/pyviz.cc
Normal file
File diff suppressed because it is too large
Load Diff
225
src/tools/visualizer/model/pyviz.h
Normal file
225
src/tools/visualizer/model/pyviz.h
Normal 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 */
|
||||
228
src/tools/visualizer/model/visual-simulator-impl.cc
Normal file
228
src/tools/visualizer/model/visual-simulator-impl.cc
Normal 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
|
||||
|
||||
|
||||
82
src/tools/visualizer/model/visual-simulator-impl.h
Normal file
82
src/tools/visualizer/model/visual-simulator-impl.h
Normal 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 */
|
||||
16
src/tools/visualizer/model/visualizer-ideas.txt
Normal file
16
src/tools/visualizer/model/visualizer-ideas.txt
Normal 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
|
||||
|
||||
3
src/tools/visualizer/visualizer/__init__.py
Normal file
3
src/tools/visualizer/visualizer/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
from core import start, register_plugin, set_bounds, add_initialization_hook
|
||||
|
||||
107
src/tools/visualizer/visualizer/base.py
Normal file
107
src/tools/visualizer/visualizer/base.py
Normal 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
|
||||
|
||||
1470
src/tools/visualizer/visualizer/core.py
Normal file
1470
src/tools/visualizer/visualizer/core.py
Normal file
File diff suppressed because it is too large
Load Diff
97
src/tools/visualizer/visualizer/higcontainer.py
Normal file
97
src/tools/visualizer/visualizer/higcontainer.py
Normal 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()
|
||||
|
||||
144
src/tools/visualizer/visualizer/hud.py
Normal file
144
src/tools/visualizer/visualizer/hud.py
Normal 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)
|
||||
296
src/tools/visualizer/visualizer/ipython_view.py
Normal file
296
src/tools/visualizer/visualizer/ipython_view.py
Normal 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)
|
||||
167
src/tools/visualizer/visualizer/plugins/interface_statistics.py
Normal file
167
src/tools/visualizer/visualizer/plugins/interface_statistics.py
Normal 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)
|
||||
@@ -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)
|
||||
102
src/tools/visualizer/visualizer/plugins/olsr.py
Normal file
102
src/tools/visualizer/visualizer/plugins/olsr.py
Normal 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)
|
||||
231
src/tools/visualizer/visualizer/plugins/show_last_packets.py
Normal file
231
src/tools/visualizer/visualizer/plugins/show_last_packets.py
Normal 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)
|
||||
@@ -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)
|
||||
311
src/tools/visualizer/visualizer/resource/Basurero_Palm_Z22.svg
Normal file
311
src/tools/visualizer/visualizer/resource/Basurero_Palm_Z22.svg
Normal 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 |
@@ -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 |
156
src/tools/visualizer/visualizer/svgitem.py
Normal file
156
src/tools/visualizer/visualizer/svgitem.py
Normal 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
|
||||
|
||||
22
src/tools/visualizer/wscript
Normal file
22
src/tools/visualizer/wscript
Normal 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 = [
|
||||
]
|
||||
@@ -62,6 +62,7 @@ all_modules = (
|
||||
'mpi',
|
||||
'contrib/topology-read',
|
||||
'contrib/energy',
|
||||
'tools/visualizer',
|
||||
)
|
||||
|
||||
def set_options(opt):
|
||||
|
||||
9
wscript
9
wscript
@@ -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:
|
||||
|
||||
13
wutils.py
13
wutils.py
@@ -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)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user