[ns3-30]Pythonスクリプトのビジュアライズを有効にする

原因が分からず、かなりハマったので備忘録です。

## 環境

OS: Ubuntu 18.04
ns3: 3.30

## 状況

ここでは例として`example/tutoral/third.py`を用います。
https://www.nsnam.org/doxygen/third_8py_source.html

なお、同じ内容の`third.cc`はvisフラグ付きで実行しても正常にビジュアライズされるものとします。

```
$ ./waf --run scratch/mythird --vis
Waf: Entering directory `/usr/ns/ns-3.30/build'
Waf: Leaving directory `/usr/ns/ns-3.30/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (0.652s)
Could not load plugin 'show_last_packets.py': No module named 'kiwi'
Could not load icon applets-screenshooter due to missing gnomedesktop Python module
scanning topology: 8 nodes...
scanning topology: calling graphviz layout
scanning topology: all done.
```

![Screenshot from 2020-05-05 21-23-38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258910/3e35cf11-a2bb-2c69-db34-a36bce393597.png)



visフラグなしでは正常に実行され、

```shell

$ ./waf --pyrun examples/tutorial/third.py
Waf: Entering directory `/usr/ns/ns-3.30/build'
Waf: Leaving directory `/usr/ns/ns-3.30/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (0.652s)
At time 2s client sent 1024 bytes to 10.1.2.4 port 9
At time 2.01799s server received 1024 bytes from 10.1.3.3 port 49153
At time 2.01799s server sent 1024 bytes to 10.1.3.3 port 49153
At time 2.03371s client received 1024 bytes from 10.1.2.4 port 9

```

visフラグをつけるとエラーが起きる。

```shell
$ ./waf --pyrun examples/tutorial/third.py --vis
Waf: Entering directory `/usr/ns/ns-3.30/build'
Waf: Leaving directory `/usr/ns/ns-3.30/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (0.707s)
assert failed. cond="uid != 0", msg="Assert in TypeId::LookupByName: ns3::VisualSimulatorImpl not found", file=../src/core/model/type-id.cc, line=828
terminate called without an active exception
Command ['/usr/bin/python3', 'examples/tutorial/third.py', '--SimulatorImplementationType=ns3::VisualSimulatorImpl'] terminated with signal SIGIOT. Run it under a debugger to get more information (./waf --run <program> --command-template="gdb --args %s <args>").

```
エラーではデバッガを使えとしか書いておらず、途方に暮れると思います。

もし`Gtk-Warning`というWarningが出る場合は、Gtkのバージョンが合っていない可能性があります。適切なGtkをインストールしましょう。

## 解決法

実はパッケージが一つ足りていません。以下のコマンドで`ns.visualizer`をimportしましょう。

```Python
import ns.visualizer
```

これはWikiのページにひっそりと書いてあります。最初から入れておいて欲しいですねw
https://www.nsnam.org/wiki/PyViz

修正後のthird.pyは以下の通り。

```Python:third.py
# -*-  Mode: Python; -*-
# /*
#  * This program is free software; you can redistribute it and/or modify
#  * it under the terms of the GNU General Public License version 2 as
#  * published by the Free Software Foundation;
#  *
#  * This program is distributed in the hope that it will be useful,
#  * but WITHOUT ANY WARRANTY; without even the implied warranty of
#  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  * GNU General Public License for more details.
#  *
#  * You should have received a copy of the GNU General Public License
#  * along with this program; if not, write to the Free Software
#  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#  *
#  * Ported to Python by Mohit P. Tahiliani
#  */

import ns.core
import ns.network
import ns.point_to_point
import ns.applications
import ns.wifi
import ns.mobility
import ns.csma
import ns.internet
import sys

import ns.visualizer

# // Default Network Topology
# //
# //   Wifi 10.1.3.0
# //                 AP
# //  *    *    *    *
# //  |    |    |    |    10.1.1.0
# // n5   n6   n7   n0 -------------- n1   n2   n3   n4
# //                   point-to-point  |    |    |    |
# //                                   ================
# //                                     LAN 10.1.2.0

cmd = ns.core.CommandLine()
cmd.nCsma = 3
cmd.verbose = "True"
cmd.nWifi = 3
cmd.tracing = "False"

cmd.AddValue("nCsma", "Number of \"extra\" CSMA nodes/devices")
cmd.AddValue("nWifi", "Number of wifi STA devices")
cmd.AddValue("verbose", "Tell echo applications to log if true")
cmd.AddValue("tracing", "Enable pcap tracing")

cmd.Parse(sys.argv)

nCsma = int(cmd.nCsma)
verbose = cmd.verbose
nWifi = int(cmd.nWifi)
tracing = cmd.tracing

# The underlying restriction of 18 is due to the grid position
# allocator's configuration; the grid layout will exceed the
# bounding box if more than 18 nodes are provided.
if nWifi > 18:
	print ("nWifi should be 18 or less; otherwise grid layout exceeds the bounding box")
	sys.exit(1)

if verbose == "True":
	ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO)
	ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO)
	
p2pNodes = ns.network.NodeContainer()
p2pNodes.Create(2)

pointToPoint = ns.point_to_point.PointToPointHelper()
pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
pointToPoint.SetChannelAttribute("Delay", ns.core.StringValue("2ms"))

p2pDevices = pointToPoint.Install(p2pNodes)

csmaNodes = ns.network.NodeContainer()
csmaNodes.Add(p2pNodes.Get(1))
csmaNodes.Create(nCsma)

csma = ns.csma.CsmaHelper()
csma.SetChannelAttribute("DataRate", ns.core.StringValue("100Mbps"))
csma.SetChannelAttribute("Delay", ns.core.TimeValue(ns.core.NanoSeconds(6560)))

csmaDevices = csma.Install(csmaNodes)

wifiStaNodes = ns.network.NodeContainer()
wifiStaNodes.Create(nWifi)
wifiApNode = p2pNodes.Get(0)

channel = ns.wifi.YansWifiChannelHelper.Default()
phy = ns.wifi.YansWifiPhyHelper.Default()
phy.SetChannel(channel.Create())

wifi = ns.wifi.WifiHelper()
wifi.SetRemoteStationManager("ns3::AarfWifiManager")

mac = ns.wifi.WifiMacHelper()
ssid = ns.wifi.Ssid ("ns-3-ssid")

mac.SetType ("ns3::StaWifiMac", "Ssid", ns.wifi.SsidValue(ssid), "ActiveProbing", ns.core.BooleanValue(False))
staDevices = wifi.Install(phy, mac, wifiStaNodes)

mac.SetType("ns3::ApWifiMac","Ssid", ns.wifi.SsidValue (ssid))
apDevices = wifi.Install(phy, mac, wifiApNode)

mobility = ns.mobility.MobilityHelper()
mobility.SetPositionAllocator ("ns3::GridPositionAllocator", "MinX", ns.core.DoubleValue(0.0), 
								"MinY", ns.core.DoubleValue (0.0), "DeltaX", ns.core.DoubleValue(5.0), "DeltaY", ns.core.DoubleValue(10.0), 
                                 "GridWidth", ns.core.UintegerValue(3), "LayoutType", ns.core.StringValue("RowFirst"))
                                 
mobility.SetMobilityModel ("ns3::RandomWalk2dMobilityModel", "Bounds", ns.mobility.RectangleValue(ns.mobility.Rectangle (-50, 50, -50, 50)))
mobility.Install(wifiStaNodes)

mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel")
mobility.Install(wifiApNode)

stack = ns.internet.InternetStackHelper()
stack.Install(csmaNodes)
stack.Install(wifiApNode)
stack.Install(wifiStaNodes)

address = ns.internet.Ipv4AddressHelper()
address.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
p2pInterfaces = address.Assign(p2pDevices)

address.SetBase(ns.network.Ipv4Address("10.1.2.0"), ns.network.Ipv4Mask("255.255.255.0"))
csmaInterfaces = address.Assign(csmaDevices)

address.SetBase(ns.network.Ipv4Address("10.1.3.0"), ns.network.Ipv4Mask("255.255.255.0"))
address.Assign(staDevices)
address.Assign(apDevices)

echoServer = ns.applications.UdpEchoServerHelper(9)

serverApps = echoServer.Install(csmaNodes.Get(nCsma))
serverApps.Start(ns.core.Seconds(1.0))
serverApps.Stop(ns.core.Seconds(10.0))

echoClient = ns.applications.UdpEchoClientHelper(csmaInterfaces.GetAddress(nCsma), 9)
echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))

clientApps = echoClient.Install(wifiStaNodes.Get (nWifi - 1))
clientApps.Start(ns.core.Seconds(2.0))
clientApps.Stop(ns.core.Seconds(10.0))

ns.internet.Ipv4GlobalRoutingHelper.PopulateRoutingTables()

ns.core.Simulator.Stop(ns.core.Seconds(10.0))

if tracing == "True":
	pointToPoint.EnablePcapAll ("third")
	phy.EnablePcap ("third", apDevices.Get (0))
	csma.EnablePcap ("third", csmaDevices.Get (0), True)

ns.core.Simulator.Run()
ns.core.Simulator.Destroy()
```

これでPythonでも無事にビジュアライザが起動するはずです。

以上