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