(Boeing r1767)

update SDT helper to support 3D display of distributed emulations (fix bug #205)
This commit is contained in:
ahrenholz 2013-09-05 17:46:12 +00:00
parent dba5f31b8d
commit f96bbf7a29
7 changed files with 168 additions and 38 deletions

View file

@ -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 * NOTE: cored is now core-daemon, and core is now core-gui (for Debian
acceptance) acceptance)
@ -45,7 +45,8 @@
- and servers file is written - and servers file is written
- fix bug #194 configuration dialog too long, make dialog scrollable/resizable - fix bug #194 configuration dialog too long, make dialog scrollable/resizable
- allow float values for loss and duplicates percent - 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 2013-04-13 CORE 4.5

View file

@ -184,6 +184,10 @@ class CoreBroker(ConfigurableManager):
(coreapi.CORE_API_ADD_FLAG | coreapi.CORE_API_LOC_FLAG): (coreapi.CORE_API_ADD_FLAG | coreapi.CORE_API_LOC_FLAG):
self.incrbootcount() self.incrbootcount()
self.session.checkruntime() 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) self.session.broadcastraw(None, data)
if count is not None and count < 1: if count is not None and count < 1:

View file

@ -670,7 +670,7 @@ class Emane(ConfigurableManager):
node.position.set(x,y,z) node.position.set(x,y,z)
msg = node.tonodemsg(flags=0) msg = node.tonodemsg(flags=0)
self.session.broadcastraw(None, msg) 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): class EmaneModel(WirelessModel):

View file

@ -232,6 +232,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
network. network.
''' '''
apitype = coreapi.CORE_NODE_RJ45 apitype = coreapi.CORE_NODE_RJ45
type = "rj45"
def __init__(self, session, objid = None, name = None, mtu = 1500, def __init__(self, session, objid = None, name = None, mtu = 1500,
verbose = False, start = True): verbose = False, start = True):
@ -245,7 +246,6 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
# the following are PyCoreNetIf attributes # the following are PyCoreNetIf attributes
self.transport_type = "raw" self.transport_type = "raw"
self.localname = name self.localname = name
self.type = "rj45"
if start: if start:
self.startup() self.startup()

View file

@ -33,6 +33,12 @@ class Sdt(object):
('tunnel','tunnel.gif'), ('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): def __init__(self, session):
self.session = session self.session = session
self.sock = None self.sock = None
@ -40,6 +46,10 @@ class Sdt(object):
self.showerror = True self.showerror = True
self.verbose = self.session.getcfgitembool('verbose', False) self.verbose = self.session.getcfgitembool('verbose', False)
self.address = ("127.0.0.1", self.DEFAULT_SDT_PORT) 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): def is_enabled(self):
if not hasattr(self.session.options, 'enablesdt'): if not hasattr(self.session.options, 'enablesdt'):
@ -49,10 +59,14 @@ class Sdt(object):
return False return False
def connect(self, flags=0): def connect(self, flags=0):
''' Connect to the SDT UDP port if enabled.
'''
if not self.is_enabled(): if not self.is_enabled():
return False return False
if self.connected: if self.connected:
return True return True
if self.session.getstate() == coreapi.CORE_EVENT_SHUTDOWN_STATE:
return False
if self.showerror: if self.showerror:
self.session.info("connecting to SDT at %s:%s" % self.address) self.session.info("connecting to SDT at %s:%s" % self.address)
if self.sock is None: if self.sock is None:
@ -73,7 +87,7 @@ class Sdt(object):
def initialize(self): def initialize(self):
''' Load icon sprites, and fly to the reference point location on ''' 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): if not self.cmd('path "%s/icons/normal"' % CORE_DATA_DIR):
return False return False
@ -95,14 +109,14 @@ class Sdt(object):
def shutdown(self): def shutdown(self):
''' Invoked from Session.shutdown() and Session.checkshutdown(). ''' Invoked from Session.shutdown() and Session.checkshutdown().
''' '''
# TODO: clear SDT display here? self.cmd('clear all')
self.disconnect() self.disconnect()
self.showerror = True self.showerror = True
def cmd(self, cmdstr): def cmd(self, cmdstr):
''' Send an SDT command over a UDP socket. socket.sendall() is used ''' Send an SDT command over a UDP socket. socket.sendall() is used
as opposed to socket.sendto() because an exception is raised when there as opposed to socket.sendto() because an exception is raised when
is no socket listener. there is no socket listener.
''' '''
if self.sock is None: if self.sock is None:
return False return False
@ -118,37 +132,38 @@ class Sdt(object):
self.connected = False self.connected = False
return 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. ''' Node is updated from a Node Message or mobility script.
''' '''
if node is None:
return
if not self.connect(): if not self.connect():
return return
if flags & coreapi.CORE_API_DEL_FLAG: 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 return
(lat, long, alt) = self.session.location.getgeo(x, y, z) (lat, long, alt) = self.session.location.getgeo(x, y, z)
pos = "pos %.6f,%.6f,%.6f" % (long, lat, alt) pos = "pos %.6f,%.6f,%.6f" % (long, lat, alt)
if flags & coreapi.CORE_API_ADD_FLAG: if flags & coreapi.CORE_API_ADD_FLAG:
type = node.type if icon is not None:
if node.icon is not None: type = name
type = node.name icon = icon.replace("$CORE_DATA_DIR", CORE_DATA_DIR)
self.cmd('sprite %s image %s' % (type, node.icon)) 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' % \ self.cmd('node %d type %s label on,"%s" %s' % \
(node.objid, type, node.name, pos)) (nodenum, type, name, pos))
else: else:
self.cmd('node %d %s' % (node.objid, pos)) self.cmd('node %d %s' % (nodenum, pos))
def updatenodegeo(self, node, lat, long, alt): def updatenodegeo(self, nodenum, lat, long, alt):
''' Node is updated upon receiving an EMANE Location Event. ''' 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(): if not self.connect():
return return
pos = "pos %.6f,%.6f,%.6f" % (long, lat, alt) 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): def updatelink(self, node1num, node2num, flags, wireless=False):
''' Link is updated from a Link Message or by a wireless model. ''' 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: elif flags & coreapi.CORE_API_ADD_FLAG:
attr = "" attr = ""
if wireless: if wireless:
attr = " line green" attr = " line green,2"
else:
attr = " line red,2"
self.cmd('link %s,%s%s' % (node1num, node2num, attr)) self.cmd('link %s,%s%s' % (node1num, node2num, attr))
def sendobjs(self): def sendobjs(self):
''' Session has already started, and the SDT3D GUI later connects. ''' Session has already started, and the SDT3D GUI later connects.
Send all node and link objects for display. Otherwise, nodes and links Send all node and link objects for display. Otherwise, nodes and
will only be drawn when they have been updated. links will only be drawn when they have been updated (e.g. moved).
''' '''
nets = [] nets = []
with self.session._objslock: with self.session._objslock:
@ -180,7 +197,14 @@ class Sdt(object):
(x, y, z) = obj.getposition() (x, y, z) = obj.getposition()
if x is None or y is None: if x is None or y is None:
continue 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: for net in nets:
# use tolinkmsgs() to handle various types of links # use tolinkmsgs() to handle various types of links
msgs = net.tolinkmsgs(flags = coreapi.CORE_API_ADD_FLAG) msgs = net.tolinkmsgs(flags = coreapi.CORE_API_ADD_FLAG)
@ -198,5 +222,115 @@ class Sdt(object):
continue continue
wl = (link_msg_type == coreapi.CORE_LINK_WIRELESS) wl = (link_msg_type == coreapi.CORE_LINK_WIRELESS)
self.updatelink(n1num, n2num, coreapi.CORE_API_ADD_FLAG, wl) 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

View file

@ -434,7 +434,7 @@ class Ns3Session(Session):
node.position.set(x, y, z) node.position.set(x, y, z)
msg = node.tonodemsg(flags=0) msg = node.tonodemsg(flags=0)
self.broadcastraw(None, msg) 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) time.sleep(0.001 * refresh_ms)
def setupmobilitytracing(self, net, filename, nodes, verbose=False): def setupmobilitytracing(self, net, filename, nodes, verbose=False):

View file

@ -432,7 +432,6 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
services_str = msg.gettlv(coreapi.CORE_TLV_NODE_SERVICES) services_str = msg.gettlv(coreapi.CORE_TLV_NODE_SERVICES)
self.session.services.addservicestonode(n, model, services_str, self.session.services.addservicestonode(n, model, services_str,
self.verbose) self.verbose)
self.session.sdt.updatenode(n, msg.flags, nodexpos, nodeypos, None)
# boot nodes if they are added after runtime (like session.bootnodes()) # boot nodes if they are added after runtime (like session.bootnodes())
if self.session.getstate() == coreapi.CORE_EVENT_RUNTIME_STATE: if self.session.getstate() == coreapi.CORE_EVENT_RUNTIME_STATE:
if isinstance(n, pycore.nodes.PyCoreNode) and \ if isinstance(n, pycore.nodes.PyCoreNode) and \
@ -452,7 +451,6 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
n = self.session.obj(nodenum) n = self.session.obj(nodenum)
except KeyError: except KeyError:
pass pass
self.session.sdt.updatenode(n, msg.flags, None, None, None)
self.session.delobj(nodenum) self.session.delobj(nodenum)
if msg.flags & coreapi.CORE_API_STR_FLAG: if msg.flags & coreapi.CORE_API_STR_FLAG:
@ -479,7 +477,6 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
else: else:
if n: if n:
n.setposition(nodexpos, nodeypos, None) n.setposition(nodexpos, nodeypos, None)
self.session.sdt.updatenode(n, msg.flags, nodexpos, nodeypos, None)
if n: if n:
if canvas is not None: if canvas is not None:
n.canvas = canvas n.canvas = canvas
@ -779,12 +776,6 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
node1.lock.release() node1.lock.release()
if node2: if node2:
node2.lock.release() 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 () return ()
def handleexecmsg(self, msg): def handleexecmsg(self, msg):