A Discrete-Event Network Simulator
API
core.py
Go to the documentation of this file.
1 # -*- Mode: python; coding: utf-8 -*-
2 from __future__ import division
3 #from __future__ import with_statement
4 
5 LAYOUT_ALGORITHM = 'neato' # ['neato'|'dot'|'twopi'|'circo'|'fdp'|'nop']
6 REPRESENT_CHANNELS_AS_NODES = 1
7 DEFAULT_NODE_SIZE = 3.0 # default node size in meters
8 DEFAULT_TRANSMISSIONS_MEMORY = 5 # default number of of past intervals whose transmissions are remembered
9 BITRATE_FONT_SIZE = 10
10 
11 # internal constants, normally not meant to be changed
12 SAMPLE_PERIOD = 0.1
13 PRIORITY_UPDATE_MODEL = -100
14 PRIORITY_UPDATE_VIEW = 200
15 
16 import platform
17 if platform.system() == "Windows":
18  SHELL_FONT = "Lucida Console 9"
19 else:
20  SHELL_FONT = "Luxi Mono 10"
21 
22 
23 import ns.core
24 import ns.network
25 import ns.visualizer
26 import ns.internet
27 import ns.mobility
28 
29 import math
30 import os
31 import sys
32 import gobject
33 import time
34 
35 try:
36  import pygraphviz
37  import gtk
38  import pango
39  import goocanvas
40  import cairo
41  import threading
42  import hud
43  #import time
44  import cairo
45  from higcontainer import HIGContainer
46  gobject.threads_init()
47  try:
48  import svgitem
49  except ImportError:
50  svgitem = None
51 except ImportError, _import_error:
52  import dummy_threading as threading
53 else:
54  _import_error = None
55 
56 try:
57  import ipython_view
58 except ImportError:
59  ipython_view = None
60 
61 from base import InformationWindow, PyVizObject, Link, lookup_netdevice_traits, PIXELS_PER_METER
62 from base import transform_distance_simulation_to_canvas, transform_point_simulation_to_canvas
63 from base import transform_distance_canvas_to_simulation, transform_point_canvas_to_simulation
64 from base import load_plugins, register_plugin, plugins
65 
66 PI_OVER_2 = math.pi/2
67 PI_TIMES_2 = math.pi*2
68 
69 ## Node class
70 class Node(PyVizObject):
71  ## @var visualizer
72  # visualier object
73  ## @var node_index
74  # node index
75  ## @var canvas_item
76  # canvas item
77  ## @var links
78  # links
79  ## @var _has_mobility
80  # has mobility model
81  ## @var _selected
82  # is selected
83  ## @var _highlighted
84  # is highlighted
85  ## @var _color
86  # color
87  ## @var _size
88  # size
89  ## @var menu
90  # menu
91  ## @var svg_item
92  # svg item
93  ## @var svg_align_x
94  # svg align X
95  ## @var svg_align_y
96  # svg align Y
97  ## @var _label
98  # label
99  ## @var _label_canvas_item
100  # label canvas
101  ## @var selected
102  # selected property
103  ## @var highlighted
104  # highlighted property
105  ## @var __gsignals__
106  # signal emitted whenever a tooltip is about to be shown for the node
107  # the first signal parameter is a python list of strings, to which information can be appended
108  __gsignals__ = {
109  'query-extra-tooltip-info': (gobject.SIGNAL_RUN_LAST, None, (object,)),
110  }
111 
112  def __init__(self, visualizer, node_index):
113  """ Initialize function.
114  @param self The object pointer.
115  @param visualizer: visualizer object
116  @param node_index: node index
117  """
118  super(Node, self).__init__()
119 
120  self.visualizer = visualizer
121  self.node_index = node_index
122  self.canvas_item = goocanvas.Ellipse()
123  self.canvas_item.set_data("pyviz-object", self)
124  self.links = []
125  self._has_mobility = None
126  self._selected = False
127  self._highlighted = False
128  self._color = 0x808080ff
129  self._size = DEFAULT_NODE_SIZE
130  self.canvas_item.connect("enter-notify-event", self.on_enter_notify_event)
131  self.canvas_item.connect("leave-notify-event", self.on_leave_notify_event)
132  self.menu = None
133  self.svg_item = None
134  self.svg_align_x = None
135  self.svg_align_y = None
136  self._label = None
137  self._label_canvas_item = None
138 
139  self._update_appearance() # call this last
140 
141  def set_svg_icon(self, file_base_name, width=None, height=None, align_x=0.5, align_y=0.5):
142  """!
143  Set a background SVG icon for the node.
144 
145  @param file_base_name: base file name, including .svg
146  extension, of the svg file. Place the file in the folder
147  src/contrib/visualizer/resource.
148 
149  @param width: scale to the specified width, in meters
150  @param height: scale to the specified height, in meters
151 
152  @param align_x: horizontal alignment of the icon relative to
153  the node position, from 0 (icon fully to the left of the node)
154  to 1.0 (icon fully to the right of the node)
155 
156  @param align_y: vertical alignment of the icon relative to the
157  node position, from 0 (icon fully to the top of the node) to
158  1.0 (icon fully to the bottom of the node)
159 
160  @return a ValueError exception if invalid dimensions.
161 
162  """
163  if width is None and height is None:
164  raise ValueError("either width or height must be given")
165  rsvg_handle = svgitem.rsvg_handle_factory(file_base_name)
166  x = self.canvas_item.props.center_x
167  y = self.canvas_item.props.center_y
168  self.svg_item = svgitem.SvgItem(x, y, rsvg_handle)
169  self.svg_item.props.parent = self.visualizer.canvas.get_root_item()
170  self.svg_item.props.pointer_events = 0
171  self.svg_item.lower(None)
172  self.svg_item.props.visibility = goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD
173  if width is not None:
174  self.svg_item.props.width = transform_distance_simulation_to_canvas(width)
175  if height is not None:
176  self.svg_item.props.height = transform_distance_simulation_to_canvas(height)
177 
178  #threshold1 = 10.0/self.svg_item.props.height
179  #threshold2 = 10.0/self.svg_item.props.width
180  #self.svg_item.props.visibility_threshold = min(threshold1, threshold2)
181 
182  self.svg_align_x = align_x
183  self.svg_align_y = align_y
184  self._update_svg_position(x, y)
185  self._update_appearance()
186 
187  def set_label(self, label):
188  """!
189  Set a label for the node.
190 
191  @param self: class object.
192  @param label: label to set
193 
194  @return: an exception if invalid parameter.
195  """
196  assert isinstance(label, basestring)
197  self._label = label
198  self._update_appearance()
199 
200  def _update_svg_position(self, x, y):
201  """!
202  Update svg position.
203 
204  @param self: class object.
205  @param x: x position
206  @param y: y position
207  @return none
208  """
209  w = self.svg_item.width
210  h = self.svg_item.height
211  self.svg_item.set_properties(x=(x - (1-self.svg_align_x)*w),
212  y=(y - (1-self.svg_align_y)*h))
213 
214 
215  def tooltip_query(self, tooltip):
216  """!
217  Query tooltip.
218 
219  @param self: class object.
220  @param tooltip: tooltip
221  @return none
222  """
223  self.visualizer.simulation.lock.acquire()
224  try:
225  ns3_node = ns.network.NodeList.GetNode(self.node_index)
226  ipv4 = ns3_node.GetObject(ns.internet.Ipv4.GetTypeId())
227  ipv6 = ns3_node.GetObject(ns.internet.Ipv6.GetTypeId())
228 
229  name = '<b><u>Node %i</u></b>' % self.node_index
230  node_name = ns.core.Names.FindName (ns3_node)
231  if len(node_name)!=0:
232  name += ' <b>(' + node_name + ')</b>'
233 
234  lines = [name]
235  lines.append('')
236 
237  self.emit("query-extra-tooltip-info", lines)
238 
239  mob = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
240  if mob is not None:
241  lines.append(' <b>Mobility Model</b>: %s' % mob.GetInstanceTypeId().GetName())
242 
243  for devI in range(ns3_node.GetNDevices()):
244  lines.append('')
245  lines.append(' <u>NetDevice %i:</u>' % devI)
246  dev = ns3_node.GetDevice(devI)
247  name = ns.core.Names.FindName(dev)
248  if name:
249  lines.append(' <b>Name:</b> %s' % name)
250  devname = dev.GetInstanceTypeId().GetName()
251  lines.append(' <b>Type:</b> %s' % devname)
252 
253  if ipv4 is not None:
254  ipv4_idx = ipv4.GetInterfaceForDevice(dev)
255  if ipv4_idx != -1:
256  addresses = [
257  '%s/%s' % (ipv4.GetAddress(ipv4_idx, i).GetLocal(),
258  ipv4.GetAddress(ipv4_idx, i).GetMask())
259  for i in range(ipv4.GetNAddresses(ipv4_idx))]
260  lines.append(' <b>IPv4 Addresses:</b> %s' % '; '.join(addresses))
261 
262  if ipv6 is not None:
263  ipv6_idx = ipv6.GetInterfaceForDevice(dev)
264  if ipv6_idx != -1:
265  addresses = [
266  '%s/%s' % (ipv6.GetAddress(ipv6_idx, i).GetAddress(),
267  ipv6.GetAddress(ipv6_idx, i).GetPrefix())
268  for i in range(ipv6.GetNAddresses(ipv6_idx))]
269  lines.append(' <b>IPv6 Addresses:</b> %s' % '; '.join(addresses))
270 
271  lines.append(' <b>MAC Address:</b> %s' % (dev.GetAddress(),))
272 
273  tooltip.set_markup('\n'.join(lines))
274  finally:
275  self.visualizer.simulation.lock.release()
276 
277  def on_enter_notify_event(self, view, target, event):
278  """!
279  On Enter event handle.
280 
281  @param self: class object.
282  @param view: view
283  @param target: target
284  @param event: event
285  @return none
286  """
287  self.highlighted = True
288  def on_leave_notify_event(self, view, target, event):
289  """!
290  On Leave event handle.
291 
292  @param self: class object.
293  @param view: view
294  @param target: target
295  @param event: event
296  @return none
297  """
298  self.highlighted = False
299 
300  def _set_selected(self, value):
301  """!
302  Set selected function.
303 
304  @param self: class object.
305  @param value: selected value
306  @return none
307  """
308  self._selected = value
309  self._update_appearance()
310  def _get_selected(self):
311  """!
312  Get selected function.
313 
314  @param self: class object.
315  @return selected status
316  """
317  return self._selected
318 
319  selected = property(_get_selected, _set_selected)
320 
321  def _set_highlighted(self, value):
322  """!
323  Set highlighted function.
324 
325  @param self: class object.
326  @param value: selected value
327  @return none
328  """
329  self._highlighted = value
330  self._update_appearance()
331  def _get_highlighted(self):
332  """!
333  Get highlighted function.
334 
335  @param self: class object.
336  @return highlighted status
337  """
338  return self._highlighted
339 
340  highlighted = property(_get_highlighted, _set_highlighted)
341 
342  def set_size(self, size):
343  """!
344  Set size function.
345 
346  @param self: class object.
347  @param size: selected size
348  @return none
349  """
350  self._size = size
351  self._update_appearance()
352 
354  """!
355  Update the node aspect to reflect the selected/highlighted state
356 
357  @param self: class object.
358  @return none
359  """
360 
362  if self.svg_item is not None:
363  alpha = 0x80
364  else:
365  alpha = 0xff
366  fill_color_rgba = (self._color & 0xffffff00) | alpha
367  self.canvas_item.set_properties(radius_x=size, radius_y=size,
368  fill_color_rgba=fill_color_rgba)
369  if self._selected:
370  line_width = size*.3
371  else:
372  line_width = size*.15
373  if self.highlighted:
374  stroke_color = 'yellow'
375  else:
376  stroke_color = 'black'
377  self.canvas_item.set_properties(line_width=line_width, stroke_color=stroke_color)
378 
379  if self._label is not None:
380  if self._label_canvas_item is None:
381  self._label_canvas_item = goocanvas.Text(visibility_threshold=0.5,
382  font="Sans Serif 10",
383  fill_color_rgba=0x808080ff,
384  alignment=pango.ALIGN_CENTER,
385  anchor=gtk.ANCHOR_N,
386  parent=self.visualizer.canvas.get_root_item(),
387  pointer_events=0)
388  self._label_canvas_item.lower(None)
389 
390  self._label_canvas_item.set_properties(visibility=goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD,
391  text=self._label)
392  self._update_position()
393 
394  def set_position(self, x, y):
395  """!
396  Set position function.
397 
398  @param self: class object.
399  @param x: x position
400  @param y: y position
401  @return none
402  """
403  self.canvas_item.set_property("center_x", x)
404  self.canvas_item.set_property("center_y", y)
405  if self.svg_item is not None:
406  self._update_svg_position(x, y)
407 
408  for link in self.links:
409  link.update_points()
410 
411  if self._label_canvas_item is not None:
412  self._label_canvas_item.set_properties(x=x, y=(y+self._size*3))
413 
414  # If the location of the point is now beyond the bounds of the
415  # canvas then those bounds now need to be increased
416  bounds = self.visualizer.canvas.get_bounds()
417 
418  (min_x, min_y, max_x, max_y) = bounds
419 
420  min_x = min(x, min_x)
421  min_y = min(y, min_y)
422  max_x = max(x, max_x)
423  max_y = max(y, max_y)
424 
425  new_bounds = (min_x, min_y, max_x, max_y)
426 
427  if new_bounds != bounds:
428  self.visualizer.canvas.set_bounds(*new_bounds)
429 
430  def get_position(self):
431  """!
432  Get position function.
433 
434  @param self: class object.
435  @return x and y position
436  """
437  return (self.canvas_item.get_property("center_x"), self.canvas_item.get_property("center_y"))
438 
439  def _update_position(self):
440  """!
441  Update position function.
442 
443  @param self: class object.
444  @return none
445  """
446  x, y = self.get_position()
447  self.set_position(x, y)
448 
449  def set_color(self, color):
450  """!
451  Set color function.
452 
453  @param self: class object.
454  @param color: color to set.
455  @return none
456  """
457  if isinstance(color, str):
458  color = gtk.gdk.color_parse(color)
459  color = ((color.red>>8) << 24) | ((color.green>>8) << 16) | ((color.blue>>8) << 8) | 0xff
460  self._color = color
461  self._update_appearance()
462 
463  def add_link(self, link):
464  """!
465  Add link function.
466 
467  @param self: class object.
468  @param link: link to add.
469  @return none
470  """
471  assert isinstance(link, Link)
472  self.links.append(link)
473 
474  def remove_link(self, link):
475  """!
476  Remove link function.
477 
478  @param self: class object.
479  @param link: link to add.
480  @return none
481  """
482  assert isinstance(link, Link)
483  self.links.remove(link)
484 
485  @property
486  def has_mobility(self):
487  """!
488  Has mobility function.
489 
490  @param self: class object.
491  @return modility option
492  """
493  if self._has_mobility is None:
494  node = ns.network.NodeList.GetNode(self.node_index)
495  mobility = node.GetObject(ns.mobility.MobilityModel.GetTypeId())
496  self._has_mobility = (mobility is not None)
497  return self._has_mobility
498 
499 
500 ## Channel
501 class Channel(PyVizObject):
502  ## @var channel
503  # channel
504  ## @var canvas_item
505  # canvas
506  ## @var links
507  # list of links
508  #
509  def __init__(self, channel):
510  """!
511  Initializer function.
512 
513  @param self: class object.
514  @param channel: channel.
515  @return none
516  """
517  self.channel = channel
518  self.canvas_item = goocanvas.Ellipse(radius_x=30, radius_y=30,
519  fill_color="white",
520  stroke_color="grey", line_width=2.0,
521  line_dash=goocanvas.LineDash([10.0, 10.0 ]),
522  visibility=goocanvas.ITEM_VISIBLE)
523  self.canvas_item.set_data("pyviz-object", self)
524  self.links = []
525 
526  def set_position(self, x, y):
527  """!
528  Initializer function.
529 
530  @param self: class object.
531  @param x: x position.
532  @param y: y position.
533  @return
534  """
535  self.canvas_item.set_property("center_x", x)
536  self.canvas_item.set_property("center_y", y)
537 
538  for link in self.links:
539  link.update_points()
540 
541  def get_position(self):
542  """!
543  Initializer function.
544 
545  @param self: class object.
546  @return x / y position.
547  """
548  return (self.canvas_item.get_property("center_x"), self.canvas_item.get_property("center_y"))
549 
550 
551 ## WiredLink
552 class WiredLink(Link):
553  ## @var node1
554  # first node
555  ## @var node2
556  # second node
557  ## @var canvas_item
558  # canvas
559  #
560  def __init__(self, node1, node2):
561  """!
562  Initializer function.
563 
564  @param self: class object.
565  @param node1: class object.
566  @param node2: class object.
567  @return none
568  """
569  assert isinstance(node1, Node)
570  assert isinstance(node2, (Node, Channel))
571  self.node1 = node1
572  self.node2 = node2
573  self.canvas_item = goocanvas.Path(line_width=1.0, stroke_color="black")
574  self.canvas_item.set_data("pyviz-object", self)
575  self.node1.links.append(self)
576  self.node2.links.append(self)
577 
578  def update_points(self):
579  """!
580  Update points function.
581 
582  @param self: class object.
583  @return none
584  """
585  pos1_x, pos1_y = self.node1.get_position()
586  pos2_x, pos2_y = self.node2.get_position()
587  self.canvas_item.set_property("data", "M %r %r L %r %r" % (pos1_x, pos1_y, pos2_x, pos2_y))
588 
589 
590 ## SimulationThread
591 class SimulationThread(threading.Thread):
592  ## @var viz
593  # Visualizer object
594  ## @var lock
595  # thread lock
596  ## @var go
597  # thread event
598  ## @var target_time
599  # in seconds
600  ## @var quit
601  # quit indicator
602  ## @var sim_helper
603  # helper function
604  ## @var pause_messages
605  # pause messages
606  def __init__(self, viz):
607  """!
608  Initializer function.
609 
610  @param self: class object.
611  @param viz: class object.
612  @return none
613  """
614  super(SimulationThread, self).__init__()
615  assert isinstance(viz, Visualizer)
616  self.viz = viz # Visualizer object
617  self.lock = threading.Lock()
618  self.go = threading.Event()
619  self.go.clear()
620  self.target_time = 0 # in seconds
621  self.quit = False
622  self.sim_helper = ns.visualizer.PyViz()
623  self.pause_messages = []
624 
625  def set_nodes_of_interest(self, nodes):
626  """!
627  Set nodes of interest function.
628 
629  @param self: class object.
630  @param nodes: class object.
631  @return
632  """
633  self.lock.acquire()
634  try:
635  self.sim_helper.SetNodesOfInterest(nodes)
636  finally:
637  self.lock.release()
638 
639  def run(self):
640  """!
641  Initializer function.
642 
643  @param self: class object.
644  @return none
645  """
646  while not self.quit:
647  #print "sim: Wait for go"
648  self.go.wait() # wait until the main (view) thread gives us the go signal
649  self.go.clear()
650  if self.quit:
651  break
652  #self.go.clear()
653  #print "sim: Acquire lock"
654  self.lock.acquire()
655  try:
656  if 0:
657  if ns3.core.Simulator.IsFinished():
658  self.viz.play_button.set_sensitive(False)
659  break
660  #print "sim: Current time is %f; Run until: %f" % (ns3.Simulator.Now ().GetSeconds (), self.target_time)
661  #if ns3.Simulator.Now ().GetSeconds () > self.target_time:
662  # print "skipping, model is ahead of view!"
663  self.sim_helper.SimulatorRunUntil(ns.core.Seconds(self.target_time))
664  #print "sim: Run until ended at current time: ", ns3.Simulator.Now ().GetSeconds ()
665  self.pause_messages.extend(self.sim_helper.GetPauseMessages())
666  gobject.idle_add(self.viz.update_model, priority=PRIORITY_UPDATE_MODEL)
667  #print "sim: Run until: ", self.target_time, ": finished."
668  finally:
669  self.lock.release()
670  #print "sim: Release lock, loop."
671 
672 ## ShowTransmissionsMode
673 class ShowTransmissionsMode(object):
674  ## @var ALL
675  # all
676  ## @var NONE
677  # none
678  ## @var SELECTED
679  # selected
680  ## @var __slots__
681  # enumeration
682  __slots__ = []
683 ShowTransmissionsMode.ALL = ShowTransmissionsMode()
684 ShowTransmissionsMode.NONE = ShowTransmissionsMode()
685 ShowTransmissionsMode.SELECTED = ShowTransmissionsMode()
686 
687 ## Visualizer
688 class Visualizer(gobject.GObject):
689  ## @var INSTANCE
690  # all
691  INSTANCE = None
692 
693  if _import_error is None:
694  __gsignals__ = {
695 
696  # signal emitted whenever a right-click-on-node popup menu is being constructed
697  'populate-node-menu': (gobject.SIGNAL_RUN_LAST, None, (object, gtk.Menu,)),
698 
699  # signal emitted after every simulation period (SAMPLE_PERIOD seconds of simulated time)
700  # the simulation lock is acquired while the signal is emitted
701  'simulation-periodic-update': (gobject.SIGNAL_RUN_LAST, None, ()),
702 
703  # signal emitted right after the topology is scanned
704  'topology-scanned': (gobject.SIGNAL_RUN_LAST, None, ()),
705 
706  # signal emitted when it's time to update the view objects
707  'update-view': (gobject.SIGNAL_RUN_LAST, None, ()),
708 
709  }
710 
711  def __init__(self):
712  """!
713  Initializer function.
714 
715  @param self: class object.
716  @return none
717  """
718  assert Visualizer.INSTANCE is None
719  Visualizer.INSTANCE = self
720  super(Visualizer, self).__init__()
721  self.nodes = {} # node index -> Node
722  self.channels = {} # id(ns3.Channel) -> Channel
723  self.window = None # toplevel window
724  self.canvas = None # goocanvas.Canvas
725  self.time_label = None # gtk.Label
726  self.play_button = None # gtk.ToggleButton
727  self.zoom = None # gtk.Adjustment
728  self._scrolled_window = None # gtk.ScrolledWindow
729 
730  self.links_group = goocanvas.Group()
731  self.channels_group = goocanvas.Group()
732  self.nodes_group = goocanvas.Group()
733 
734  self._update_timeout_id = None
735  self.simulation = SimulationThread(self)
736  self.selected_node = None # node currently selected
737  self.speed = 1.0
738  self.information_windows = []
739  self._transmission_arrows = []
740  self._last_transmissions = []
741  self._drop_arrows = []
742  self._last_drops = []
743  self._show_transmissions_mode = None
744  self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
745  self._panning_state = None
746  self.node_size_adjustment = None
747  self.transmissions_smoothing_adjustment = None
748  self.sample_period = SAMPLE_PERIOD
749  self.node_drag_state = None
750  self.follow_node = None
751  self.shell_window = None
752 
753  self.create_gui()
754 
755  for plugin in plugins:
756  plugin(self)
757 
758  def set_show_transmissions_mode(self, mode):
759  """!
760  Set show transmission mode.
761 
762  @param self: class object.
763  @param mode: mode to set.
764  @return none
765  """
766  assert isinstance(mode, ShowTransmissionsMode)
767  self._show_transmissions_mode = mode
768  if self._show_transmissions_mode == ShowTransmissionsMode.ALL:
769  self.simulation.set_nodes_of_interest(range(ns.network.NodeList.GetNNodes()))
770  elif self._show_transmissions_mode == ShowTransmissionsMode.NONE:
771  self.simulation.set_nodes_of_interest([])
772  elif self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
773  if self.selected_node is None:
774  self.simulation.set_nodes_of_interest([])
775  else:
776  self.simulation.set_nodes_of_interest([self.selected_node.node_index])
777 
778  def _create_advanced_controls(self):
779  """!
780  Create advanced controls.
781 
782  @param self: class object.
783  @return expander
784  """
785  expander = gtk.Expander("Advanced")
786  expander.show()
787 
788  main_vbox = gobject.new(gtk.VBox, border_width=8, visible=True)
789  expander.add(main_vbox)
790 
791  main_hbox1 = gobject.new(gtk.HBox, border_width=8, visible=True)
792  main_vbox.pack_start(main_hbox1)
793 
794  show_transmissions_group = HIGContainer("Show transmissions")
795  show_transmissions_group.show()
796  main_hbox1.pack_start(show_transmissions_group, False, False, 8)
797 
798  vbox = gtk.VBox(True, 4)
799  vbox.show()
800  show_transmissions_group.add(vbox)
801 
802  all_nodes = gtk.RadioButton(None)
803  all_nodes.set_label("All nodes")
804  all_nodes.set_active(True)
805  all_nodes.show()
806  vbox.add(all_nodes)
807 
808  selected_node = gtk.RadioButton(all_nodes)
809  selected_node.show()
810  selected_node.set_label("Selected node")
811  selected_node.set_active(False)
812  vbox.add(selected_node)
813 
814  no_node = gtk.RadioButton(all_nodes)
815  no_node.show()
816  no_node.set_label("Disabled")
817  no_node.set_active(False)
818  vbox.add(no_node)
819 
820  def toggled(radio):
821  if radio.get_active():
822  self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
823  all_nodes.connect("toggled", toggled)
824 
825  def toggled(radio):
826  if radio.get_active():
827  self.set_show_transmissions_mode(ShowTransmissionsMode.NONE)
828  no_node.connect("toggled", toggled)
829 
830  def toggled(radio):
831  if radio.get_active():
832  self.set_show_transmissions_mode(ShowTransmissionsMode.SELECTED)
833  selected_node.connect("toggled", toggled)
834 
835 
836  # -- misc settings
837  misc_settings_group = HIGContainer("Misc Settings")
838  misc_settings_group.show()
839  main_hbox1.pack_start(misc_settings_group, False, False, 8)
840  settings_hbox = gobject.new(gtk.HBox, border_width=8, visible=True)
841  misc_settings_group.add(settings_hbox)
842 
843  # --> node size
844  vbox = gobject.new(gtk.VBox, border_width=0, visible=True)
845  scale = gobject.new(gtk.HScale, visible=True, digits=2)
846  vbox.pack_start(scale, True, True, 0)
847  vbox.pack_start(gobject.new(gtk.Label, label="Node Size", visible=True), True, True, 0)
848  settings_hbox.pack_start(vbox, False, False, 6)
849  self.node_size_adjustment = scale.get_adjustment()
850  def node_size_changed(adj):
851  for node in self.nodes.itervalues():
852  node.set_size(adj.value)
853  self.node_size_adjustment.connect("value-changed", node_size_changed)
854  self.node_size_adjustment.set_all(DEFAULT_NODE_SIZE, 0.01, 20, 0.1)
855 
856  # --> transmissions smooth factor
857  vbox = gobject.new(gtk.VBox, border_width=0, visible=True)
858  scale = gobject.new(gtk.HScale, visible=True, digits=1)
859  vbox.pack_start(scale, True, True, 0)
860  vbox.pack_start(gobject.new(gtk.Label, label="Tx. Smooth Factor (s)", visible=True), True, True, 0)
861  settings_hbox.pack_start(vbox, False, False, 6)
862  self.transmissions_smoothing_adjustment = scale.get_adjustment()
863  self.transmissions_smoothing_adjustment.set_all(DEFAULT_TRANSMISSIONS_MEMORY*0.1, 0.1, 10, 0.1)
864 
865  return expander
866 
867  ## PanningState class
868  class _PanningState(object):
869  ## @var __slots__
870  # internal variables
871  __slots__ = ['initial_mouse_pos', 'initial_canvas_pos', 'motion_signal']
872 
873  def _begin_panning(self, widget, event):
874  """!
875  Set show trnamission mode.
876 
877  @param self: class object.
878  @param mode: mode to set.
879  @return none
880  """
881  self.canvas.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))
882  self._panning_state = self._PanningState()
883  x, y, dummy = widget.window.get_pointer()
884  self._panning_state.initial_mouse_pos = (x, y)
885  x = self._scrolled_window.get_hadjustment().value
886  y = self._scrolled_window.get_vadjustment().value
887  self._panning_state.initial_canvas_pos = (x, y)
888  self._panning_state.motion_signal = self.canvas.connect("motion-notify-event", self._panning_motion)
889 
890  def _end_panning(self, event):
891  """!
892  End panning function.
893 
894  @param self: class object.
895  @param event: active event.
896  @return none
897  """
898  if self._panning_state is None:
899  return
900  self.canvas.window.set_cursor(None)
901  self.canvas.disconnect(self._panning_state.motion_signal)
902  self._panning_state = None
903 
904  def _panning_motion(self, widget, event):
905  """!
906  Panning motion function.
907 
908  @param self: class object.
909  @param widget: widget.
910  @param event: event.
911  @return true if successful
912  """
913  assert self._panning_state is not None
914  if event.is_hint:
915  x, y, dummy = widget.window.get_pointer()
916  else:
917  x, y = event.x, event.y
918 
919  hadj = self._scrolled_window.get_hadjustment()
920  vadj = self._scrolled_window.get_vadjustment()
921  mx0, my0 = self._panning_state.initial_mouse_pos
922  cx0, cy0 = self._panning_state.initial_canvas_pos
923 
924  dx = x - mx0
925  dy = y - my0
926  hadj.value = cx0 - dx
927  vadj.value = cy0 - dy
928  return True
929 
930  def _canvas_button_press(self, widget, event):
931  if event.button == 2:
932  self._begin_panning(widget, event)
933  return True
934  return False
935 
936  def _canvas_button_release(self, dummy_widget, event):
937  if event.button == 2:
938  self._end_panning(event)
939  return True
940  return False
941 
942  def _canvas_scroll_event(self, dummy_widget, event):
943  if event.direction == gtk.gdk.SCROLL_UP:
944  self.zoom.value *= 1.25
945  return True
946  elif event.direction == gtk.gdk.SCROLL_DOWN:
947  self.zoom.value /= 1.25
948  return True
949  return False
950 
951  def get_hadjustment(self):
952  return self._scrolled_window.get_hadjustment()
953  def get_vadjustment(self):
954  return self._scrolled_window.get_vadjustment()
955 
956  def create_gui(self):
957  self.window = gtk.Window()
958  vbox = gtk.VBox(); vbox.show()
959  self.window.add(vbox)
960 
961  # canvas
962  self.canvas = goocanvas.Canvas()
963  self.canvas.connect_after("button-press-event", self._canvas_button_press)
964  self.canvas.connect_after("button-release-event", self._canvas_button_release)
965  self.canvas.connect("scroll-event", self._canvas_scroll_event)
966  self.canvas.props.has_tooltip = True
967  self.canvas.connect("query-tooltip", self._canvas_tooltip_cb)
968  self.canvas.show()
969  sw = gtk.ScrolledWindow(); sw.show()
970  self._scrolled_window = sw
971  sw.add(self.canvas)
972  vbox.pack_start(sw, True, True, 4)
973  self.canvas.set_size_request(600, 450)
974  self.canvas.set_bounds(-10000, -10000, 10000, 10000)
975  self.canvas.scroll_to(0, 0)
976 
977 
978  self.canvas.get_root_item().add_child(self.links_group)
979  self.links_group.set_property("visibility", goocanvas.ITEM_VISIBLE)
980 
981  self.canvas.get_root_item().add_child(self.channels_group)
982  self.channels_group.set_property("visibility", goocanvas.ITEM_VISIBLE)
983  self.channels_group.raise_(self.links_group)
984 
985  self.canvas.get_root_item().add_child(self.nodes_group)
986  self.nodes_group.set_property("visibility", goocanvas.ITEM_VISIBLE)
987  self.nodes_group.raise_(self.channels_group)
988 
989  self.hud = hud.Axes(self)
990 
991  hbox = gtk.HBox(); hbox.show()
992  vbox.pack_start(hbox, False, False, 4)
993 
994  # zoom
995  zoom_adj = gtk.Adjustment(1.0, 0.01, 10.0, 0.02, 1.0, 0)
996  self.zoom = zoom_adj
997  def _zoom_changed(adj):
998  self.canvas.set_scale(adj.value)
999  zoom_adj.connect("value-changed", _zoom_changed)
1000  zoom = gtk.SpinButton(zoom_adj)
1001  zoom.set_digits(3)
1002  zoom.show()
1003  hbox.pack_start(gobject.new(gtk.Label, label=" Zoom:", visible=True), False, False, 4)
1004  hbox.pack_start(zoom, False, False, 4)
1005  _zoom_changed(zoom_adj)
1006 
1007  # speed
1008  speed_adj = gtk.Adjustment(1.0, 0.01, 10.0, 0.02, 1.0, 0)
1009  def _speed_changed(adj):
1010  self.speed = adj.value
1011  self.sample_period = SAMPLE_PERIOD*adj.value
1012  self._start_update_timer()
1013  speed_adj.connect("value-changed", _speed_changed)
1014  speed = gtk.SpinButton(speed_adj)
1015  speed.set_digits(3)
1016  speed.show()
1017  hbox.pack_start(gobject.new(gtk.Label, label=" Speed:", visible=True), False, False, 4)
1018  hbox.pack_start(speed, False, False, 4)
1019  _speed_changed(speed_adj)
1020 
1021  # Current time
1022  self.time_label = gobject.new(gtk.Label, label=" Speed:", visible=True)
1023  self.time_label.set_width_chars(20)
1024  hbox.pack_start(self.time_label, False, False, 4)
1025 
1026  # Screenshot button
1027  screenshot_button = gobject.new(gtk.Button,
1028  label="Snapshot",
1029  relief=gtk.RELIEF_NONE, focus_on_click=False,
1030  visible=True)
1031  hbox.pack_start(screenshot_button, False, False, 4)
1032 
1033  def load_button_icon(button, icon_name):
1034  try:
1035  import gnomedesktop
1036  except ImportError:
1037  sys.stderr.write("Could not load icon %s due to missing gnomedesktop Python module\n" % icon_name)
1038  else:
1039  icon = gnomedesktop.find_icon(gtk.icon_theme_get_default(), icon_name, 16, 0)
1040  if icon is not None:
1041  button.props.image = gobject.new(gtk.Image, file=icon, visible=True)
1042 
1043  load_button_icon(screenshot_button, "applets-screenshooter")
1044  screenshot_button.connect("clicked", self._take_screenshot)
1045 
1046  # Shell button
1047  if ipython_view is not None:
1048  shell_button = gobject.new(gtk.Button,
1049  label="Shell",
1050  relief=gtk.RELIEF_NONE, focus_on_click=False,
1051  visible=True)
1052  hbox.pack_start(shell_button, False, False, 4)
1053  load_button_icon(shell_button, "gnome-terminal")
1054  shell_button.connect("clicked", self._start_shell)
1055 
1056  # Play button
1057  self.play_button = gobject.new(gtk.ToggleButton,
1058  image=gobject.new(gtk.Image, stock=gtk.STOCK_MEDIA_PLAY, visible=True),
1059  label="Simulate (F3)",
1060  relief=gtk.RELIEF_NONE, focus_on_click=False,
1061  use_stock=True, visible=True)
1062  accel_group = gtk.AccelGroup()
1063  self.window.add_accel_group(accel_group)
1064  self.play_button.add_accelerator("clicked", accel_group,
1065  gtk.keysyms.F3, 0, gtk.ACCEL_VISIBLE)
1066  self.play_button.connect("toggled", self._on_play_button_toggled)
1067  hbox.pack_start(self.play_button, False, False, 4)
1068 
1069  self.canvas.get_root_item().connect("button-press-event", self.on_root_button_press_event)
1070 
1071  vbox.pack_start(self._create_advanced_controls(), False, False, 4)
1072 
1073  self.window.show()
1074 
1075  def scan_topology(self):
1076  print "scanning topology: %i nodes..." % (ns.network.NodeList.GetNNodes(),)
1077  graph = pygraphviz.AGraph()
1078  seen_nodes = 0
1079  for nodeI in range(ns.network.NodeList.GetNNodes()):
1080  seen_nodes += 1
1081  if seen_nodes == 100:
1082  print "scan topology... %i nodes visited (%.1f%%)" % (nodeI, 100*nodeI/ns.network.NodeList.GetNNodes())
1083  seen_nodes = 0
1084  node = ns.network.NodeList.GetNode(nodeI)
1085  node_name = "Node %i" % nodeI
1086  node_view = self.get_node(nodeI)
1087 
1088  mobility = node.GetObject(ns.mobility.MobilityModel.GetTypeId())
1089  if mobility is not None:
1090  node_view.set_color("red")
1091  pos = mobility.GetPosition()
1092  node_view.set_position(*transform_point_simulation_to_canvas(pos.x, pos.y))
1093  #print "node has mobility position -> ", "%f,%f" % (pos.x, pos.y)
1094  else:
1095  graph.add_node(node_name)
1096 
1097  for devI in range(node.GetNDevices()):
1098  device = node.GetDevice(devI)
1099  device_traits = lookup_netdevice_traits(type(device))
1100  if device_traits.is_wireless:
1101  continue
1102  if device_traits.is_virtual:
1103  continue
1104  channel = device.GetChannel()
1105  if channel.GetNDevices() > 2:
1106  if REPRESENT_CHANNELS_AS_NODES:
1107  # represent channels as white nodes
1108  if mobility is None:
1109  channel_name = "Channel %s" % id(channel)
1110  graph.add_edge(node_name, channel_name)
1111  self.get_channel(channel)
1112  self.create_link(self.get_node(nodeI), self.get_channel(channel))
1113  else:
1114  # don't represent channels, just add links between nodes in the same channel
1115  for otherDevI in range(channel.GetNDevices()):
1116  otherDev = channel.GetDevice(otherDevI)
1117  otherNode = otherDev.GetNode()
1118  otherNodeView = self.get_node(otherNode.GetId())
1119  if otherNode is not node:
1120  if mobility is None and not otherNodeView.has_mobility:
1121  other_node_name = "Node %i" % otherNode.GetId()
1122  graph.add_edge(node_name, other_node_name)
1123  self.create_link(self.get_node(nodeI), otherNodeView)
1124  else:
1125  for otherDevI in range(channel.GetNDevices()):
1126  otherDev = channel.GetDevice(otherDevI)
1127  otherNode = otherDev.GetNode()
1128  otherNodeView = self.get_node(otherNode.GetId())
1129  if otherNode is not node:
1130  if mobility is None and not otherNodeView.has_mobility:
1131  other_node_name = "Node %i" % otherNode.GetId()
1132  graph.add_edge(node_name, other_node_name)
1133  self.create_link(self.get_node(nodeI), otherNodeView)
1134 
1135  print "scanning topology: calling graphviz layout"
1136  graph.layout(LAYOUT_ALGORITHM)
1137  for node in graph.iternodes():
1138  #print node, "=>", node.attr['pos']
1139  node_type, node_id = node.split(' ')
1140  pos_x, pos_y = [float(s) for s in node.attr['pos'].split(',')]
1141  if node_type == 'Node':
1142  obj = self.nodes[int(node_id)]
1143  elif node_type == 'Channel':
1144  obj = self.channels[int(node_id)]
1145  obj.set_position(pos_x, pos_y)
1146 
1147  print "scanning topology: all done."
1148  self.emit("topology-scanned")
1149 
1150  def get_node(self, index):
1151  try:
1152  return self.nodes[index]
1153  except KeyError:
1154  node = Node(self, index)
1155  self.nodes[index] = node
1156  self.nodes_group.add_child(node.canvas_item)
1157  node.canvas_item.connect("button-press-event", self.on_node_button_press_event, node)
1158  node.canvas_item.connect("button-release-event", self.on_node_button_release_event, node)
1159  return node
1160 
1161  def get_channel(self, ns3_channel):
1162  try:
1163  return self.channels[id(ns3_channel)]
1164  except KeyError:
1165  channel = Channel(ns3_channel)
1166  self.channels[id(ns3_channel)] = channel
1167  self.channels_group.add_child(channel.canvas_item)
1168  return channel
1169 
1170  def create_link(self, node, node_or_channel):
1171  link = WiredLink(node, node_or_channel)
1172  self.links_group.add_child(link.canvas_item)
1173  link.canvas_item.lower(None)
1174 
1175  def update_view(self):
1176  #print "update_view"
1177 
1178  self.time_label.set_text("Time: %f s" % ns.core.Simulator.Now().GetSeconds())
1179 
1180  self._update_node_positions()
1181 
1182  # Update information
1183  for info_win in self.information_windows:
1184  info_win.update()
1185 
1186  self._update_transmissions_view()
1187  self._update_drops_view()
1188 
1189  self.emit("update-view")
1190 
1191  def _update_node_positions(self):
1192  for node in self.nodes.itervalues():
1193  if node.has_mobility:
1194  ns3_node = ns.network.NodeList.GetNode(node.node_index)
1195  mobility = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
1196  if mobility is not None:
1197  pos = mobility.GetPosition()
1198  x, y = transform_point_simulation_to_canvas(pos.x, pos.y)
1199  node.set_position(x, y)
1200  if node is self.follow_node:
1201  hadj = self._scrolled_window.get_hadjustment()
1202  vadj = self._scrolled_window.get_vadjustment()
1203  px, py = self.canvas.convert_to_pixels(x, y)
1204  hadj.value = px - hadj.page_size/2
1205  vadj.value = py - vadj.page_size/2
1206 
1207  def center_on_node(self, node):
1208  if isinstance(node, ns.network.Node):
1209  node = self.nodes[node.GetId()]
1210  elif isinstance(node, (int, long)):
1211  node = self.nodes[node]
1212  elif isinstance(node, Node):
1213  pass
1214  else:
1215  raise TypeError("expected int, viz.Node or ns.network.Node, not %r" % node)
1216 
1217  x, y = node.get_position()
1218  hadj = self._scrolled_window.get_hadjustment()
1219  vadj = self._scrolled_window.get_vadjustment()
1220  px, py = self.canvas.convert_to_pixels(x, y)
1221  hadj.value = px - hadj.page_size/2
1222  vadj.value = py - vadj.page_size/2
1223 
1224 
1225  def update_model(self):
1226  self.simulation.lock.acquire()
1227  try:
1228  self.emit("simulation-periodic-update")
1229  finally:
1230  self.simulation.lock.release()
1231 
1232  def do_simulation_periodic_update(self):
1233  smooth_factor = int(self.transmissions_smoothing_adjustment.value*10)
1234 
1235  transmissions = self.simulation.sim_helper.GetTransmissionSamples()
1236  self._last_transmissions.append(transmissions)
1237  while len(self._last_transmissions) > smooth_factor:
1238  self._last_transmissions.pop(0)
1239 
1240  drops = self.simulation.sim_helper.GetPacketDropSamples()
1241  self._last_drops.append(drops)
1242  while len(self._last_drops) > smooth_factor:
1243  self._last_drops.pop(0)
1244 
1245  def _get_label_over_line_position(self, pos1_x, pos1_y, pos2_x, pos2_y):
1246  hadj = self._scrolled_window.get_hadjustment()
1247  vadj = self._scrolled_window.get_vadjustment()
1248  bounds_x1, bounds_y1 = self.canvas.convert_from_pixels(hadj.value, vadj.value)
1249  bounds_x2, bounds_y2 = self.canvas.convert_from_pixels(hadj.value + hadj.page_size,
1250  vadj.value + vadj.page_size)
1251  pos1_x, pos1_y, pos2_x, pos2_y = ns.visualizer.PyViz.LineClipping(bounds_x1, bounds_y1,
1252  bounds_x2, bounds_y2,
1253  pos1_x, pos1_y,
1254  pos2_x, pos2_y)
1255  return (pos1_x + pos2_x)/2, (pos1_y + pos2_y)/2
1256 
1257  def _update_transmissions_view(self):
1258  transmissions_average = {}
1259  for transmission_set in self._last_transmissions:
1260  for transmission in transmission_set:
1261  key = (transmission.transmitter.GetId(), transmission.receiver.GetId())
1262  rx_bytes, count = transmissions_average.get(key, (0, 0))
1263  rx_bytes += transmission.bytes
1264  count += 1
1265  transmissions_average[key] = rx_bytes, count
1266 
1267  old_arrows = self._transmission_arrows
1268  for arrow, label in old_arrows:
1269  arrow.set_property("visibility", goocanvas.ITEM_HIDDEN)
1270  label.set_property("visibility", goocanvas.ITEM_HIDDEN)
1271  new_arrows = []
1272 
1273  k = self.node_size_adjustment.value/5
1274 
1275  for (transmitter_id, receiver_id), (rx_bytes, rx_count) in transmissions_average.iteritems():
1276  transmitter = self.get_node(transmitter_id)
1277  receiver = self.get_node(receiver_id)
1278  try:
1279  arrow, label = old_arrows.pop()
1280  except IndexError:
1281  arrow = goocanvas.Polyline(line_width=2.0, stroke_color_rgba=0x00C000C0, close_path=False, end_arrow=True)
1282  arrow.set_property("parent", self.canvas.get_root_item())
1283  arrow.props.pointer_events = 0
1284  arrow.raise_(None)
1285 
1286  label = goocanvas.Text(parent=self.canvas.get_root_item(), pointer_events=0)
1287  label.raise_(None)
1288 
1289  arrow.set_property("visibility", goocanvas.ITEM_VISIBLE)
1290  line_width = max(0.1, math.log(float(rx_bytes)/rx_count/self.sample_period)*k)
1291  arrow.set_property("line-width", line_width)
1292 
1293  pos1_x, pos1_y = transmitter.get_position()
1294  pos2_x, pos2_y = receiver.get_position()
1295  points = goocanvas.Points([(pos1_x, pos1_y), (pos2_x, pos2_y)])
1296  arrow.set_property("points", points)
1297 
1298  kbps = float(rx_bytes*8)/1e3/rx_count/self.sample_period
1299  label.set_properties(visibility=goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD,
1300  visibility_threshold=0.5,
1301  font=("Sans Serif %f" % int(1+BITRATE_FONT_SIZE*k)))
1302  angle = math.atan2((pos2_y - pos1_y), (pos2_x - pos1_x))
1303  if -PI_OVER_2 <= angle <= PI_OVER_2:
1304  label.set_properties(text=("%.2f kbit/s →" % (kbps,)),
1305  alignment=pango.ALIGN_CENTER,
1306  anchor=gtk.ANCHOR_S,
1307  x=0, y=-line_width/2)
1308  M = cairo.Matrix()
1309  M.translate(*self._get_label_over_line_position(pos1_x, pos1_y, pos2_x, pos2_y))
1310  M.rotate(angle)
1311  label.set_transform(M)
1312  else:
1313  label.set_properties(text=("← %.2f kbit/s" % (kbps,)),
1314  alignment=pango.ALIGN_CENTER,
1315  anchor=gtk.ANCHOR_N,
1316  x=0, y=line_width/2)
1317  M = cairo.Matrix()
1318  M.translate(*self._get_label_over_line_position(pos1_x, pos1_y, pos2_x, pos2_y))
1319  M.rotate(angle)
1320  M.scale(-1, -1)
1321  label.set_transform(M)
1322 
1323  new_arrows.append((arrow, label))
1324 
1325  self._transmission_arrows = new_arrows + old_arrows
1326 
1327 
1328  def _update_drops_view(self):
1329  drops_average = {}
1330  for drop_set in self._last_drops:
1331  for drop in drop_set:
1332  key = drop.transmitter.GetId()
1333  drop_bytes, count = drops_average.get(key, (0, 0))
1334  drop_bytes += drop.bytes
1335  count += 1
1336  drops_average[key] = drop_bytes, count
1337 
1338  old_arrows = self._drop_arrows
1339  for arrow, label in old_arrows:
1340  arrow.set_property("visibility", goocanvas.ITEM_HIDDEN)
1341  label.set_property("visibility", goocanvas.ITEM_HIDDEN)
1342  new_arrows = []
1343 
1344  # get the coordinates for the edge of screen
1345  vadjustment = self._scrolled_window.get_vadjustment()
1346  bottom_y = vadjustment.value + vadjustment.page_size
1347  dummy, edge_y = self.canvas.convert_from_pixels(0, bottom_y)
1348 
1349  k = self.node_size_adjustment.value/5
1350 
1351  for transmitter_id, (drop_bytes, drop_count) in drops_average.iteritems():
1352  transmitter = self.get_node(transmitter_id)
1353  try:
1354  arrow, label = old_arrows.pop()
1355  except IndexError:
1356  arrow = goocanvas.Polyline(line_width=2.0, stroke_color_rgba=0xC00000C0, close_path=False, end_arrow=True)
1357  arrow.props.pointer_events = 0
1358  arrow.set_property("parent", self.canvas.get_root_item())
1359  arrow.raise_(None)
1360 
1361  label = goocanvas.Text()#, fill_color_rgba=0x00C000C0)
1362  label.props.pointer_events = 0
1363  label.set_property("parent", self.canvas.get_root_item())
1364  label.raise_(None)
1365 
1366  arrow.set_property("visibility", goocanvas.ITEM_VISIBLE)
1367  arrow.set_property("line-width", max(0.1, math.log(float(drop_bytes)/drop_count/self.sample_period)*k))
1368  pos1_x, pos1_y = transmitter.get_position()
1369  pos2_x, pos2_y = pos1_x, edge_y
1370  points = goocanvas.Points([(pos1_x, pos1_y), (pos2_x, pos2_y)])
1371  arrow.set_property("points", points)
1372 
1373  label.set_properties(visibility=goocanvas.ITEM_VISIBLE_ABOVE_THRESHOLD,
1374  visibility_threshold=0.5,
1375  font=("Sans Serif %i" % int(1+BITRATE_FONT_SIZE*k)),
1376  text=("%.2f kbit/s" % (float(drop_bytes*8)/1e3/drop_count/self.sample_period,)),
1377  alignment=pango.ALIGN_CENTER,
1378  x=(pos1_x + pos2_x)/2,
1379  y=(pos1_y + pos2_y)/2)
1380 
1381  new_arrows.append((arrow, label))
1382 
1383  self._drop_arrows = new_arrows + old_arrows
1384 
1385 
1386  def update_view_timeout(self):
1387  #print "view: update_view_timeout called at real time ", time.time()
1388 
1389  # while the simulator is busy, run the gtk event loop
1390  while not self.simulation.lock.acquire(False):
1391  while gtk.events_pending():
1392  gtk.main_iteration()
1393  pause_messages = self.simulation.pause_messages
1394  self.simulation.pause_messages = []
1395  try:
1396  self.update_view()
1397  self.simulation.target_time = ns.core.Simulator.Now ().GetSeconds () + self.sample_period
1398  #print "view: target time set to %f" % self.simulation.target_time
1399  finally:
1400  self.simulation.lock.release()
1401 
1402  if pause_messages:
1403  #print pause_messages
1404  dialog = gtk.MessageDialog(parent=self.window, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_OK,
1405  message_format='\n'.join(pause_messages))
1406  dialog.connect("response", lambda d, r: d.destroy())
1407  dialog.show()
1408  self.play_button.set_active(False)
1409 
1410  # if we're paused, stop the update timer
1411  if not self.play_button.get_active():
1412  self._update_timeout_id = None
1413  return False
1414 
1415  #print "view: self.simulation.go.set()"
1416  self.simulation.go.set()
1417  #print "view: done."
1418  return True
1419 
1420  def _start_update_timer(self):
1421  if self._update_timeout_id is not None:
1422  gobject.source_remove(self._update_timeout_id)
1423  #print "start_update_timer"
1424  self._update_timeout_id = gobject.timeout_add(int(SAMPLE_PERIOD/min(self.speed, 1)*1e3),
1425  self.update_view_timeout,
1426  priority=PRIORITY_UPDATE_VIEW)
1427 
1428  def _on_play_button_toggled(self, button):
1429  if button.get_active():
1430  self._start_update_timer()
1431  else:
1432  if self._update_timeout_id is not None:
1433  gobject.source_remove(self._update_timeout_id)
1434 
1435  def _quit(self, *dummy_args):
1436  if self._update_timeout_id is not None:
1437  gobject.source_remove(self._update_timeout_id)
1438  self._update_timeout_id = None
1439  self.simulation.quit = True
1440  self.simulation.go.set()
1441  self.simulation.join()
1442  gtk.main_quit()
1443 
1444  def _monkey_patch_ipython(self):
1445  # The user may want to access the NS 3 simulation state, but
1446  # NS 3 is not thread safe, so it could cause serious problems.
1447  # To work around this, monkey-patch IPython to automatically
1448  # acquire and release the simulation lock around each code
1449  # that is executed.
1450 
1451  original_runcode = self.ipython.runcode
1452  def runcode(ip, *args):
1453  #print "lock"
1454  self.simulation.lock.acquire()
1455  try:
1456  return original_runcode(*args)
1457  finally:
1458  #print "unlock"
1459  self.simulation.lock.release()
1460  import types
1461  self.ipython.runcode = types.MethodType(runcode, self.ipython)
1462 
1463  def autoscale_view(self):
1464  if not self.nodes:
1465  return
1466  self._update_node_positions()
1467  positions = [node.get_position() for node in self.nodes.itervalues()]
1468  min_x, min_y = min(x for (x,y) in positions), min(y for (x,y) in positions)
1469  max_x, max_y = max(x for (x,y) in positions), max(y for (x,y) in positions)
1470  min_x_px, min_y_px = self.canvas.convert_to_pixels(min_x, min_y)
1471  max_x_px, max_y_px = self.canvas.convert_to_pixels(max_x, max_y)
1472  dx = max_x - min_x
1473  dy = max_y - min_y
1474  dx_px = max_x_px - min_x_px
1475  dy_px = max_y_px - min_y_px
1476  hadj = self._scrolled_window.get_hadjustment()
1477  vadj = self._scrolled_window.get_vadjustment()
1478  new_dx, new_dy = 1.5*dx_px, 1.5*dy_px
1479 
1480  if new_dx == 0 or new_dy == 0:
1481  return
1482 
1483  self.zoom.value = min(hadj.page_size/new_dx, vadj.page_size/new_dy)
1484 
1485  x1, y1 = self.canvas.convert_from_pixels(hadj.value, vadj.value)
1486  x2, y2 = self.canvas.convert_from_pixels(hadj.value+hadj.page_size, vadj.value+vadj.page_size)
1487  width = x2 - x1
1488  height = y2 - y1
1489  center_x = (min_x + max_x) / 2
1490  center_y = (min_y + max_y) / 2
1491 
1492  self.canvas.scroll_to(center_x - width/2, center_y - height/2)
1493 
1494  return False
1495 
1496  def start(self):
1497  self.scan_topology()
1498  self.window.connect("delete-event", self._quit)
1499  #self._start_update_timer()
1500  gobject.timeout_add(200, self.autoscale_view)
1501  self.simulation.start()
1502 
1503  try:
1504  __IPYTHON__
1505  except NameError:
1506  pass
1507  else:
1508  self._monkey_patch_ipython()
1509 
1510  gtk.main()
1511 
1512 
1513  def on_root_button_press_event(self, view, target, event):
1514  if event.button == 1:
1515  self.select_node(None)
1516  return True
1517 
1518  def on_node_button_press_event(self, view, target, event, node):
1519  if event.button == 1:
1520  self.select_node(node)
1521  return True
1522  elif event.button == 3:
1523  self.popup_node_menu(node, event)
1524  return True
1525  elif event.button == 2:
1526  self.begin_node_drag(node)
1527  return True
1528  return False
1529 
1530  def on_node_button_release_event(self, view, target, event, node):
1531  if event.button == 2:
1532  self.end_node_drag(node)
1533  return True
1534  return False
1535 
1536  class NodeDragState(object):
1537  def __init__(self, canvas_x0, canvas_y0, sim_x0, sim_y0):
1538  self.canvas_x0 = canvas_x0
1539  self.canvas_y0 = canvas_y0
1540  self.sim_x0 = sim_x0
1541  self.sim_y0 = sim_y0
1542  self.motion_signal = None
1543 
1544  def begin_node_drag(self, node):
1545  self.simulation.lock.acquire()
1546  try:
1547  ns3_node = ns.network.NodeList.GetNode(node.node_index)
1548  mob = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
1549  if mob is None:
1550  return
1551  if self.node_drag_state is not None:
1552  return
1553  pos = mob.GetPosition()
1554  finally:
1555  self.simulation.lock.release()
1556  x, y, dummy = self.canvas.window.get_pointer()
1557  x0, y0 = self.canvas.convert_from_pixels(x, y)
1558  self.node_drag_state = self.NodeDragState(x0, y0, pos.x, pos.y)
1559  self.node_drag_state.motion_signal = node.canvas_item.connect("motion-notify-event", self.node_drag_motion, node)
1560 
1561  def node_drag_motion(self, item, targe_item, event, node):
1562  self.simulation.lock.acquire()
1563  try:
1564  ns3_node = ns.network.NodeList.GetNode(node.node_index)
1565  mob = ns3_node.GetObject(ns.mobility.MobilityModel.GetTypeId())
1566  if mob is None:
1567  return False
1568  if self.node_drag_state is None:
1569  return False
1570  x, y, dummy = self.canvas.window.get_pointer()
1571  canvas_x, canvas_y = self.canvas.convert_from_pixels(x, y)
1572  dx = (canvas_x - self.node_drag_state.canvas_x0)
1573  dy = (canvas_y - self.node_drag_state.canvas_y0)
1574  pos = mob.GetPosition()
1575  pos.x = self.node_drag_state.sim_x0 + transform_distance_canvas_to_simulation(dx)
1576  pos.y = self.node_drag_state.sim_y0 + transform_distance_canvas_to_simulation(dy)
1577  #print "SetPosition(%G, %G)" % (pos.x, pos.y)
1578  mob.SetPosition(pos)
1579  node.set_position(*transform_point_simulation_to_canvas(pos.x, pos.y))
1580  finally:
1581  self.simulation.lock.release()
1582  return True
1583 
1584  def end_node_drag(self, node):
1585  if self.node_drag_state is None:
1586  return
1587  node.canvas_item.disconnect(self.node_drag_state.motion_signal)
1588  self.node_drag_state = None
1589 
1590  def popup_node_menu(self, node, event):
1591  menu = gtk.Menu()
1592  self.emit("populate-node-menu", node, menu)
1593  menu.popup(None, None, None, event.button, event.time)
1594 
1595  def _update_ipython_selected_node(self):
1596  # If we are running under ipython -gthread, make this new
1597  # selected node available as a global 'selected_node'
1598  # variable.
1599  try:
1600  __IPYTHON__
1601  except NameError:
1602  pass
1603  else:
1604  if self.selected_node is None:
1605  ns3_node = None
1606  else:
1607  self.simulation.lock.acquire()
1608  try:
1609  ns3_node = ns.network.NodeList.GetNode(self.selected_node.node_index)
1610  finally:
1611  self.simulation.lock.release()
1612  self.ipython.updateNamespace({'selected_node': ns3_node})
1613 
1614 
1615  def select_node(self, node):
1616  if isinstance(node, ns.network.Node):
1617  node = self.nodes[node.GetId()]
1618  elif isinstance(node, (int, long)):
1619  node = self.nodes[node]
1620  elif isinstance(node, Node):
1621  pass
1622  elif node is None:
1623  pass
1624  else:
1625  raise TypeError("expected None, int, viz.Node or ns.network.Node, not %r" % node)
1626 
1627  if node is self.selected_node:
1628  return
1629 
1630  if self.selected_node is not None:
1631  self.selected_node.selected = False
1632  self.selected_node = node
1633  if self.selected_node is not None:
1634  self.selected_node.selected = True
1635 
1636  if self._show_transmissions_mode == ShowTransmissionsMode.SELECTED:
1637  if self.selected_node is None:
1638  self.simulation.set_nodes_of_interest([])
1639  else:
1640  self.simulation.set_nodes_of_interest([self.selected_node.node_index])
1641 
1642  self._update_ipython_selected_node()
1643 
1644 
1645  def add_information_window(self, info_win):
1646  self.information_windows.append(info_win)
1647  self.simulation.lock.acquire()
1648  try:
1649  info_win.update()
1650  finally:
1651  self.simulation.lock.release()
1652 
1653  def remove_information_window(self, info_win):
1654  self.information_windows.remove(info_win)
1655 
1656  def _canvas_tooltip_cb(self, canvas, x, y, keyboard_mode, tooltip):
1657  #print "tooltip query: ", x, y
1658  hadj = self._scrolled_window.get_hadjustment()
1659  vadj = self._scrolled_window.get_vadjustment()
1660  x, y = self.canvas.convert_from_pixels(hadj.value + x, vadj.value + y)
1661  item = self.canvas.get_item_at(x, y, True)
1662  #print "items at (%f, %f): %r | keyboard_mode=%r" % (x, y, item, keyboard_mode)
1663  if not item:
1664  return False
1665  while item is not None:
1666  obj = item.get_data("pyviz-object")
1667  if obj is not None:
1668  obj.tooltip_query(tooltip)
1669  return True
1670  item = item.props.parent
1671  return False
1672 
1673  def _get_export_file_name(self):
1674  sel = gtk.FileChooserDialog("Save...", self.canvas.get_toplevel(),
1675  gtk.FILE_CHOOSER_ACTION_SAVE,
1676  (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
1677  gtk.STOCK_SAVE, gtk.RESPONSE_OK))
1678  sel.set_default_response(gtk.RESPONSE_OK)
1679  sel.set_local_only(True)
1680  sel.set_do_overwrite_confirmation(True)
1681  sel.set_current_name("Unnamed.pdf")
1682 
1683  filter = gtk.FileFilter()
1684  filter.set_name("Embedded PostScript")
1685  filter.add_mime_type("image/x-eps")
1686  sel.add_filter(filter)
1687 
1688  filter = gtk.FileFilter()
1689  filter.set_name("Portable Document Graphics")
1690  filter.add_mime_type("application/pdf")
1691  sel.add_filter(filter)
1692 
1693  filter = gtk.FileFilter()
1694  filter.set_name("Scalable Vector Graphics")
1695  filter.add_mime_type("image/svg+xml")
1696  sel.add_filter(filter)
1697 
1698  resp = sel.run()
1699  if resp != gtk.RESPONSE_OK:
1700  sel.destroy()
1701  return None
1702 
1703  file_name = sel.get_filename()
1704  sel.destroy()
1705  return file_name
1706 
1707  def _take_screenshot(self, dummy_button):
1708  #print "Cheese!"
1709  file_name = self._get_export_file_name()
1710  if file_name is None:
1711  return
1712 
1713  # figure out the correct bounding box for what is visible on screen
1714  x1 = self._scrolled_window.get_hadjustment().value
1715  y1 = self._scrolled_window.get_vadjustment().value
1716  x2 = x1 + self._scrolled_window.get_hadjustment().page_size
1717  y2 = y1 + self._scrolled_window.get_vadjustment().page_size
1718  bounds = goocanvas.Bounds()
1719  bounds.x1, bounds.y1 = self.canvas.convert_from_pixels(x1, y1)
1720  bounds.x2, bounds.y2 = self.canvas.convert_from_pixels(x2, y2)
1721  dest_width = bounds.x2 - bounds.x1
1722  dest_height = bounds.y2 - bounds.y1
1723  #print bounds.x1, bounds.y1, " -> ", bounds.x2, bounds.y2
1724 
1725  dummy, extension = os.path.splitext(file_name)
1726  extension = extension.lower()
1727  if extension == '.eps':
1728  surface = cairo.PSSurface(file_name, dest_width, dest_height)
1729  elif extension == '.pdf':
1730  surface = cairo.PDFSurface(file_name, dest_width, dest_height)
1731  elif extension == '.svg':
1732  surface = cairo.SVGSurface(file_name, dest_width, dest_height)
1733  else:
1734  dialog = gtk.MessageDialog(parent = self.canvas.get_toplevel(),
1735  flags = gtk.DIALOG_DESTROY_WITH_PARENT,
1736  type = gtk.MESSAGE_ERROR,
1737  buttons = gtk.BUTTONS_OK,
1738  message_format = "Unknown extension '%s' (valid extensions are '.eps', '.svg', and '.pdf')"
1739  % (extension,))
1740  dialog.run()
1741  dialog.destroy()
1742  return
1743 
1744  # draw the canvas to a printing context
1745  cr = cairo.Context(surface)
1746  cr.translate(-bounds.x1, -bounds.y1)
1747  self.canvas.render(cr, bounds, self.zoom.value)
1748  cr.show_page()
1749  surface.finish()
1750 
1751  def set_follow_node(self, node):
1752  if isinstance(node, ns.network.Node):
1753  node = self.nodes[node.GetId()]
1754  self.follow_node = node
1755 
1756  def _start_shell(self, dummy_button):
1757  if self.shell_window is not None:
1758  self.shell_window.present()
1759  return
1760 
1761  self.shell_window = gtk.Window()
1762  self.shell_window.set_size_request(750,550)
1763  self.shell_window.set_resizable(True)
1764  scrolled_window = gtk.ScrolledWindow()
1765  scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
1766  self.ipython = ipython_view.IPythonView()
1767  self.ipython.modify_font(pango.FontDescription(SHELL_FONT))
1768  self.ipython.set_wrap_mode(gtk.WRAP_CHAR)
1769  self.ipython.show()
1770  scrolled_window.add(self.ipython)
1771  scrolled_window.show()
1772  self.shell_window.add(scrolled_window)
1773  self.shell_window.show()
1774  self.shell_window.connect('destroy', self._on_shell_window_destroy)
1775 
1776  self._update_ipython_selected_node()
1777  self.ipython.updateNamespace({'viz': self})
1778 
1779 
1780  def _on_shell_window_destroy(self, window):
1781  self.shell_window = None
1782 
1783 
1784 initialization_hooks = []
1785 
1786 def add_initialization_hook(hook, *args):
1787  """
1788  Adds a callback to be called after
1789  the visualizer is initialized, like this::
1790  initialization_hook(visualizer, *args)
1791  """
1792  global initialization_hooks
1793  initialization_hooks.append((hook, args))
1794 
1795 
1796 def set_bounds(x1, y1, x2, y2):
1797  assert x2>x1
1798  assert y2>y1
1799  def hook(viz):
1800  cx1, cy1 = transform_point_simulation_to_canvas(x1, y1)
1801  cx2, cy2 = transform_point_simulation_to_canvas(x2, y2)
1802  viz.canvas.set_bounds(cx1, cy1, cx2, cy2)
1804 
1805 
1806 def start():
1807  assert Visualizer.INSTANCE is None
1808  if _import_error is not None:
1809  import sys
1810  print >> sys.stderr, "No visualization support (%s)." % (str(_import_error),)
1811  ns.core.Simulator.Run()
1812  return
1813  load_plugins()
1814  viz = Visualizer()
1815  for hook, args in initialization_hooks:
1816  gobject.idle_add(hook, viz, *args)
1817  ns.network.Packet.EnablePrinting()
1818  viz.start()
ShowTransmissionsMode.
Definition: core.py:673
_label_canvas_item
label canvas
Definition: core.py:137
def on_enter_notify_event(self, view, target, event)
On Enter event handle.
Definition: core.py:277
Node class.
Definition: core.py:70
def transform_distance_simulation_to_canvas(d)
Definition: base.py:84
def __init__(self, viz)
Initializer function.
Definition: core.py:606
node_index
node index
Definition: core.py:121
#define min(a, b)
Definition: 80211b.c:42
def _set_selected(self, value)
Set selected function.
Definition: core.py:300
_highlighted
is highlighted
Definition: core.py:127
def set_position(self, x, y)
Set position function.
Definition: core.py:394
def __init__(self, channel)
Initializer function.
Definition: core.py:509
def set_svg_icon(self, file_base_name, width=None, height=None, align_x=0.5, align_y=0.5)
Set a background SVG icon for the node.
Definition: core.py:141
def start()
Definition: core.py:1806
svg_item
svg item
Definition: core.py:133
def add_link(self, link)
Add link function.
Definition: core.py:463
def set_label(self, label)
Set a label for the node.
Definition: core.py:187
def _update_svg_position(self, x, y)
Update svg position.
Definition: core.py:200
def _update_appearance(self)
Update the node aspect to reflect the selected/highlighted state.
Definition: core.py:353
SimulationThread.
Definition: core.py:591
#define max(a, b)
Definition: 80211b.c:43
def get_position(self)
Get position function.
Definition: core.py:430
svg_align_x
svg align X
Definition: core.py:134
pause_messages
pause messages
Definition: core.py:623
def on_leave_notify_event(self, view, target, event)
On Leave event handle.
Definition: core.py:288
def transform_point_simulation_to_canvas(x, y)
Definition: base.py:87
_has_mobility
has mobility model
Definition: core.py:125
def set_bounds(x1, y1, x2, y2)
Definition: core.py:1796
highlighted
highlighted property
Definition: core.py:340
def _update_position(self)
Update position function.
Definition: core.py:439
links
list of links
Definition: core.py:524
def load_plugins()
Definition: base.py:115
visualizer
visualier object
Definition: core.py:120
def get_position(self)
Initializer function.
Definition: core.py:541
def remove_link(self, link)
Remove link function.
Definition: core.py:474
def _get_highlighted(self)
Get highlighted function.
Definition: core.py:331
viz
Visualizer object.
Definition: core.py:616
svg_align_y
svg align Y
Definition: core.py:135
canvas_item
canvas item
Definition: core.py:122
def add_initialization_hook(hook, args)
Definition: core.py:1786
def tooltip_query(self, tooltip)
Query tooltip.
Definition: core.py:215
def __init__(self, visualizer, node_index)
Definition: core.py:112
def set_position(self, x, y)
Initializer function.
Definition: core.py:526
def set_color(self, color)
Set color function.
Definition: core.py:449
def lookup_netdevice_traits(class_type)
Definition: base.py:72
Axes class.
Definition: hud.py:9
def _get_selected(self)
Get selected function.
Definition: core.py:310
sim_helper
helper function
Definition: core.py:622
def _set_highlighted(self, value)
Set highlighted function.
Definition: core.py:321
def set_nodes_of_interest(self, nodes)
Set nodes of interest function.
Definition: core.py:625
def has_mobility(self)
Has mobility function.
Definition: core.py:486
SvgItem class.
Definition: svgitem.py:9
_selected
is selected
Definition: core.py:126
def run(self)
Initializer function.
Definition: core.py:639
def set_size(self, size)
Set size function.
Definition: core.py:342
def transform_distance_canvas_to_simulation(d)
Definition: base.py:90