diff --git a/Changelog b/Changelog index 9d116ad7..5451d9b3 100644 --- a/Changelog +++ b/Changelog @@ -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 diff --git a/daemon/core/broker.py b/daemon/core/broker.py index 5c6c6cb6..f3bcf72b 100644 --- a/daemon/core/broker.py +++ b/daemon/core/broker.py @@ -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: diff --git a/daemon/core/emane/emane.py b/daemon/core/emane/emane.py index d6d9931f..76dfbce7 100644 --- a/daemon/core/emane/emane.py +++ b/daemon/core/emane/emane.py @@ -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): diff --git a/daemon/core/netns/nodes.py b/daemon/core/netns/nodes.py index 0ab9cb5f..a644fc06 100644 --- a/daemon/core/netns/nodes.py +++ b/daemon/core/netns/nodes.py @@ -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() diff --git a/daemon/core/sdt.py b/daemon/core/sdt.py index 71fc8411..fb1c9300 100644 --- a/daemon/core/sdt.py +++ b/daemon/core/sdt.py @@ -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 diff --git a/daemon/ns3/corens3/obj.py b/daemon/ns3/corens3/obj.py index 8bde3668..7148ff0a 100644 --- a/daemon/ns3/corens3/obj.py +++ b/daemon/ns3/corens3/obj.py @@ -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): diff --git a/daemon/sbin/core-daemon b/daemon/sbin/core-daemon index 7733492a..a3ea7a2d 100755 --- a/daemon/sbin/core-daemon +++ b/daemon/sbin/core-daemon @@ -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):