(Boeing r1767)
update SDT helper to support 3D display of distributed emulations (fix bug #205)
This commit is contained in:
parent
dba5f31b8d
commit
f96bbf7a29
7 changed files with 168 additions and 38 deletions
|
@ -1,4 +1,4 @@
|
|||
2013-08-12 CORE 4.6
|
||||
2013-09-05 CORE 4.6
|
||||
|
||||
* NOTE: cored is now core-daemon, and core is now core-gui (for Debian
|
||||
acceptance)
|
||||
|
@ -45,7 +45,8 @@
|
|||
- and servers file is written
|
||||
- fix bug #194 configuration dialog too long, make dialog scrollable/resizable
|
||||
- allow float values for loss and duplicates percent
|
||||
- fix the following bugs: 166, 172, 177, 178, 192, 194, 196, 201, 206, 210, 212, 213, 214, 221
|
||||
- fix the following bugs: 166, 172, 177, 178, 192, 194, 196, 201, 202,
|
||||
205, 206, 210, 212, 213, 214, 221
|
||||
|
||||
2013-04-13 CORE 4.5
|
||||
|
||||
|
|
|
@ -184,6 +184,10 @@ class CoreBroker(ConfigurableManager):
|
|||
(coreapi.CORE_API_ADD_FLAG | coreapi.CORE_API_LOC_FLAG):
|
||||
self.incrbootcount()
|
||||
self.session.checkruntime()
|
||||
elif msgtype == coreapi.CORE_API_LINK_MSG:
|
||||
# this allows green link lines for remote WLANs
|
||||
msg = coreapi.CoreLinkMessage(msgflags, msghdr, msgdata)
|
||||
self.session.sdt.handledistributed(msg)
|
||||
|
||||
self.session.broadcastraw(None, data)
|
||||
if count is not None and count < 1:
|
||||
|
|
|
@ -670,7 +670,7 @@ class Emane(ConfigurableManager):
|
|||
node.position.set(x,y,z)
|
||||
msg = node.tonodemsg(flags=0)
|
||||
self.session.broadcastraw(None, msg)
|
||||
self.session.sdt.updatenodegeo(node, lat, long, alt)
|
||||
self.session.sdt.updatenodegeo(node.objid, lat, long, alt)
|
||||
|
||||
|
||||
class EmaneModel(WirelessModel):
|
||||
|
|
|
@ -232,6 +232,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
|||
network.
|
||||
'''
|
||||
apitype = coreapi.CORE_NODE_RJ45
|
||||
type = "rj45"
|
||||
|
||||
def __init__(self, session, objid = None, name = None, mtu = 1500,
|
||||
verbose = False, start = True):
|
||||
|
@ -245,7 +246,6 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
|||
# the following are PyCoreNetIf attributes
|
||||
self.transport_type = "raw"
|
||||
self.localname = name
|
||||
self.type = "rj45"
|
||||
if start:
|
||||
self.startup()
|
||||
|
||||
|
|
|
@ -33,6 +33,12 @@ class Sdt(object):
|
|||
('tunnel','tunnel.gif'),
|
||||
]
|
||||
|
||||
class Bunch:
|
||||
''' Helper class for recording a collection of attributes.
|
||||
'''
|
||||
def __init__(self, **kwds):
|
||||
self.__dict__.update(kwds)
|
||||
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
self.sock = None
|
||||
|
@ -40,6 +46,10 @@ class Sdt(object):
|
|||
self.showerror = True
|
||||
self.verbose = self.session.getcfgitembool('verbose', False)
|
||||
self.address = ("127.0.0.1", self.DEFAULT_SDT_PORT)
|
||||
# node information for remote nodes not in session._objs
|
||||
# local nodes also appear here since their obj may not exist yet
|
||||
self.remotes = {}
|
||||
session.broker.handlers += (self.handledistributed, )
|
||||
|
||||
def is_enabled(self):
|
||||
if not hasattr(self.session.options, 'enablesdt'):
|
||||
|
@ -49,10 +59,14 @@ class Sdt(object):
|
|||
return False
|
||||
|
||||
def connect(self, flags=0):
|
||||
''' Connect to the SDT UDP port if enabled.
|
||||
'''
|
||||
if not self.is_enabled():
|
||||
return False
|
||||
if self.connected:
|
||||
return True
|
||||
if self.session.getstate() == coreapi.CORE_EVENT_SHUTDOWN_STATE:
|
||||
return False
|
||||
if self.showerror:
|
||||
self.session.info("connecting to SDT at %s:%s" % self.address)
|
||||
if self.sock is None:
|
||||
|
@ -73,7 +87,7 @@ class Sdt(object):
|
|||
|
||||
def initialize(self):
|
||||
''' Load icon sprites, and fly to the reference point location on
|
||||
the virtual globe.
|
||||
the virtual globe.
|
||||
'''
|
||||
if not self.cmd('path "%s/icons/normal"' % CORE_DATA_DIR):
|
||||
return False
|
||||
|
@ -95,14 +109,14 @@ class Sdt(object):
|
|||
def shutdown(self):
|
||||
''' Invoked from Session.shutdown() and Session.checkshutdown().
|
||||
'''
|
||||
# TODO: clear SDT display here?
|
||||
self.cmd('clear all')
|
||||
self.disconnect()
|
||||
self.showerror = True
|
||||
|
||||
def cmd(self, cmdstr):
|
||||
''' Send an SDT command over a UDP socket. socket.sendall() is used
|
||||
as opposed to socket.sendto() because an exception is raised when there
|
||||
is no socket listener.
|
||||
as opposed to socket.sendto() because an exception is raised when
|
||||
there is no socket listener.
|
||||
'''
|
||||
if self.sock is None:
|
||||
return False
|
||||
|
@ -118,37 +132,38 @@ class Sdt(object):
|
|||
self.connected = False
|
||||
return False
|
||||
|
||||
def updatenode(self, node, flags, x, y, z):
|
||||
def updatenode(self, nodenum, flags, x, y, z,
|
||||
name=None, type=None, icon=None):
|
||||
''' Node is updated from a Node Message or mobility script.
|
||||
'''
|
||||
if node is None:
|
||||
return
|
||||
if not self.connect():
|
||||
return
|
||||
if flags & coreapi.CORE_API_DEL_FLAG:
|
||||
self.cmd('delete node,%d' % node.objid)
|
||||
self.cmd('delete node,%d' % nodenum)
|
||||
return
|
||||
if x is None or y is None:
|
||||
return
|
||||
(lat, long, alt) = self.session.location.getgeo(x, y, z)
|
||||
pos = "pos %.6f,%.6f,%.6f" % (long, lat, alt)
|
||||
if flags & coreapi.CORE_API_ADD_FLAG:
|
||||
type = node.type
|
||||
if node.icon is not None:
|
||||
type = node.name
|
||||
self.cmd('sprite %s image %s' % (type, node.icon))
|
||||
if icon is not None:
|
||||
type = name
|
||||
icon = icon.replace("$CORE_DATA_DIR", CORE_DATA_DIR)
|
||||
icon = icon.replace("$CORE_CONF_DIR", CORE_CONF_DIR)
|
||||
self.cmd('sprite %s image %s' % (type, icon))
|
||||
self.cmd('node %d type %s label on,"%s" %s' % \
|
||||
(node.objid, type, node.name, pos))
|
||||
(nodenum, type, name, pos))
|
||||
else:
|
||||
self.cmd('node %d %s' % (node.objid, pos))
|
||||
|
||||
def updatenodegeo(self, node, lat, long, alt):
|
||||
self.cmd('node %d %s' % (nodenum, pos))
|
||||
|
||||
def updatenodegeo(self, nodenum, lat, long, alt):
|
||||
''' Node is updated upon receiving an EMANE Location Event.
|
||||
TODO: received Node Message with lat/long/alt.
|
||||
'''
|
||||
if node is None:
|
||||
return
|
||||
if not self.connect():
|
||||
return
|
||||
pos = "pos %.6f,%.6f,%.6f" % (long, lat, alt)
|
||||
self.cmd('node %d %s' % (node.objid, pos))
|
||||
self.cmd('node %d %s' % (nodenum, pos))
|
||||
|
||||
def updatelink(self, node1num, node2num, flags, wireless=False):
|
||||
''' Link is updated from a Link Message or by a wireless model.
|
||||
|
@ -162,13 +177,15 @@ class Sdt(object):
|
|||
elif flags & coreapi.CORE_API_ADD_FLAG:
|
||||
attr = ""
|
||||
if wireless:
|
||||
attr = " line green"
|
||||
attr = " line green,2"
|
||||
else:
|
||||
attr = " line red,2"
|
||||
self.cmd('link %s,%s%s' % (node1num, node2num, attr))
|
||||
|
||||
def sendobjs(self):
|
||||
''' Session has already started, and the SDT3D GUI later connects.
|
||||
Send all node and link objects for display. Otherwise, nodes and links
|
||||
will only be drawn when they have been updated.
|
||||
Send all node and link objects for display. Otherwise, nodes and
|
||||
links will only be drawn when they have been updated (e.g. moved).
|
||||
'''
|
||||
nets = []
|
||||
with self.session._objslock:
|
||||
|
@ -180,7 +197,14 @@ class Sdt(object):
|
|||
(x, y, z) = obj.getposition()
|
||||
if x is None or y is None:
|
||||
continue
|
||||
self.updatenode(obj, coreapi.CORE_API_ADD_FLAG, x, y, z)
|
||||
self.updatenode(obj.objid, coreapi.CORE_API_ADD_FLAG, x, y, z,
|
||||
obj.name, obj.type, obj.icon)
|
||||
for nodenum in sorted(self.remotes.keys()):
|
||||
r = self.remotes[nodenum]
|
||||
(x, y, z) = r.pos
|
||||
self.updatenode(nodenum, coreapi.CORE_API_ADD_FLAG, x, y, z,
|
||||
r.name, r.type, r.icon)
|
||||
|
||||
for net in nets:
|
||||
# use tolinkmsgs() to handle various types of links
|
||||
msgs = net.tolinkmsgs(flags = coreapi.CORE_API_ADD_FLAG)
|
||||
|
@ -198,5 +222,115 @@ class Sdt(object):
|
|||
continue
|
||||
wl = (link_msg_type == coreapi.CORE_LINK_WIRELESS)
|
||||
self.updatelink(n1num, n2num, coreapi.CORE_API_ADD_FLAG, wl)
|
||||
|
||||
for n1num in sorted(self.remotes.keys()):
|
||||
r = self.remotes[n1num]
|
||||
for (n2num, wl) in r.links:
|
||||
self.updatelink(n1num, n2num, coreapi.CORE_API_ADD_FLAG, wl)
|
||||
|
||||
def handledistributed(self, msg):
|
||||
''' Broker handler for processing CORE API messages as they are
|
||||
received. This is used to snoop the Node messages and update
|
||||
node positions.
|
||||
'''
|
||||
if msg.msgtype == coreapi.CORE_API_LINK_MSG:
|
||||
return self.handlelinkmsg(msg)
|
||||
elif msg.msgtype == coreapi.CORE_API_NODE_MSG:
|
||||
return self.handlenodemsg(msg)
|
||||
|
||||
def handlenodemsg(self, msg):
|
||||
''' Process a Node Message to add/delete or move a node on
|
||||
the SDT display. Node properties are found in session._objs or
|
||||
self.remotes for remote nodes (or those not yet instantiated).
|
||||
'''
|
||||
# for distributed sessions to work properly, the SDT option should be
|
||||
# enabled prior to starting the session
|
||||
if not self.is_enabled():
|
||||
return False
|
||||
# node.(objid, type, icon, name) are used.
|
||||
nodenum = msg.gettlv(coreapi.CORE_TLV_NODE_NUMBER)
|
||||
if not nodenum:
|
||||
return
|
||||
x = msg.gettlv(coreapi.CORE_TLV_NODE_XPOS)
|
||||
y = msg.gettlv(coreapi.CORE_TLV_NODE_YPOS)
|
||||
z = None
|
||||
name = msg.gettlv(coreapi.CORE_TLV_NODE_NAME)
|
||||
|
||||
nodetype = msg.gettlv(coreapi.CORE_TLV_NODE_TYPE)
|
||||
model = msg.gettlv(coreapi.CORE_TLV_NODE_MODEL)
|
||||
icon = msg.gettlv(coreapi.CORE_TLV_NODE_ICON)
|
||||
|
||||
net = False
|
||||
if nodetype == coreapi.CORE_NODE_DEF or \
|
||||
nodetype == coreapi.CORE_NODE_PHYS or \
|
||||
nodetype == coreapi.CORE_NODE_XEN:
|
||||
if model is None:
|
||||
model = "router"
|
||||
type = model
|
||||
elif nodetype != None:
|
||||
type = coreapi.node_class(nodetype).type
|
||||
net = True
|
||||
else:
|
||||
type = None
|
||||
|
||||
try:
|
||||
node = self.session.obj(nodenum)
|
||||
except KeyError:
|
||||
node = None
|
||||
if node:
|
||||
self.updatenode(node.objid, msg.flags, x, y, z,
|
||||
node.name, node.type, node.icon)
|
||||
else:
|
||||
if nodenum in self.remotes:
|
||||
remote = self.remotes[nodenum]
|
||||
if name is None:
|
||||
name = remote.name
|
||||
if type is None:
|
||||
type = remote.type
|
||||
if icon is None:
|
||||
icon = remote.icon
|
||||
else:
|
||||
remote = self.Bunch(objid=nodenum, type=type, icon=icon,
|
||||
name=name, net=net, links=set())
|
||||
self.remotes[nodenum] = remote
|
||||
remote.pos = (x, y, z)
|
||||
self.updatenode(nodenum, msg.flags, x, y, z, name, type, icon)
|
||||
|
||||
def handlelinkmsg(self, msg):
|
||||
''' Process a Link Message to add/remove links on the SDT display.
|
||||
Links are recorded in the remotes[nodenum1].links set for updating
|
||||
the SDT display at a later time.
|
||||
'''
|
||||
if not self.is_enabled():
|
||||
return False
|
||||
nodenum1 = msg.gettlv(coreapi.CORE_TLV_LINK_N1NUMBER)
|
||||
nodenum2 = msg.gettlv(coreapi.CORE_TLV_LINK_N2NUMBER)
|
||||
link_msg_type = msg.gettlv(coreapi.CORE_TLV_LINK_TYPE)
|
||||
# this filters out links to WLAN and EMANE nodes which are not drawn
|
||||
if self.wlancheck(nodenum1):
|
||||
return
|
||||
wl = (link_msg_type == coreapi.CORE_LINK_WIRELESS)
|
||||
if nodenum1 in self.remotes:
|
||||
r = self.remotes[nodenum1]
|
||||
if msg.flags & coreapi.CORE_API_DEL_FLAG:
|
||||
if (nodenum2, wl) in r.links:
|
||||
r.links.remove((nodenum2, wl))
|
||||
else:
|
||||
r.links.add((nodenum2, wl))
|
||||
self.updatelink(nodenum1, nodenum2, msg.flags, wireless=wl)
|
||||
|
||||
def wlancheck(self, nodenum):
|
||||
''' Helper returns True if a node number corresponds to a WlanNode
|
||||
or EmaneNode.
|
||||
'''
|
||||
if nodenum in self.remotes:
|
||||
type = self.remotes[nodenum].type
|
||||
if type in ("wlan", "emane"):
|
||||
return True
|
||||
else:
|
||||
try:
|
||||
n = self.session.obj(nodenum)
|
||||
except KeyError:
|
||||
return False
|
||||
if isinstance(n, (pycore.nodes.WlanNode, pycore.nodes.EmaneNode)):
|
||||
return True
|
||||
return False
|
||||
|
|
|
@ -434,7 +434,7 @@ class Ns3Session(Session):
|
|||
node.position.set(x, y, z)
|
||||
msg = node.tonodemsg(flags=0)
|
||||
self.broadcastraw(None, msg)
|
||||
self.sdt.updatenode(node, flags=0, x=x, y=y, z=z)
|
||||
self.sdt.updatenode(node.objid, flags=0, x=x, y=y, z=z)
|
||||
time.sleep(0.001 * refresh_ms)
|
||||
|
||||
def setupmobilitytracing(self, net, filename, nodes, verbose=False):
|
||||
|
|
|
@ -432,7 +432,6 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
|
|||
services_str = msg.gettlv(coreapi.CORE_TLV_NODE_SERVICES)
|
||||
self.session.services.addservicestonode(n, model, services_str,
|
||||
self.verbose)
|
||||
self.session.sdt.updatenode(n, msg.flags, nodexpos, nodeypos, None)
|
||||
# boot nodes if they are added after runtime (like session.bootnodes())
|
||||
if self.session.getstate() == coreapi.CORE_EVENT_RUNTIME_STATE:
|
||||
if isinstance(n, pycore.nodes.PyCoreNode) and \
|
||||
|
@ -452,7 +451,6 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
|
|||
n = self.session.obj(nodenum)
|
||||
except KeyError:
|
||||
pass
|
||||
self.session.sdt.updatenode(n, msg.flags, None, None, None)
|
||||
self.session.delobj(nodenum)
|
||||
|
||||
if msg.flags & coreapi.CORE_API_STR_FLAG:
|
||||
|
@ -479,7 +477,6 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
|
|||
else:
|
||||
if n:
|
||||
n.setposition(nodexpos, nodeypos, None)
|
||||
self.session.sdt.updatenode(n, msg.flags, nodexpos, nodeypos, None)
|
||||
if n:
|
||||
if canvas is not None:
|
||||
n.canvas = canvas
|
||||
|
@ -779,12 +776,6 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
|
|||
node1.lock.release()
|
||||
if node2:
|
||||
node2.lock.release()
|
||||
if not isinstance(net, pycore.nodes.WlanNode) and \
|
||||
not isinstance(net, pycore.nodes.EmaneNode):
|
||||
# show links in SDT display except for links to WLAN clouds
|
||||
wl = (link_msg_type == coreapi.CORE_LINK_WIRELESS)
|
||||
self.session.sdt.updatelink(nodenum1, nodenum2, msg.flags,
|
||||
wireless=wl)
|
||||
return ()
|
||||
|
||||
def handleexecmsg(self, msg):
|
||||
|
|
Loading…
Add table
Reference in a new issue