@@ -291,7 +286,6 @@
queueJson = $.parseJSON(json.Queue);
nbrsJson = $.parseJSON(json.Neighbors);
kaPeriodJson = $.parseJSON(json.kaPeriod)[0];
- joinedJson = $.parseJSON(json.Joined)[0];
}
// Exclude tailing description from server
@@ -303,7 +297,6 @@
$("#sync_fld").text( hasJson && syncJson.isSync > 0 ? 'Synchronized!' : 'Not synchronized');
$("#pan_fld").text( hasJson ? idJson.myPANID : '');
$("#asn_fld").text( hasJson ? asnJson.asn : '');
- $("#join_fld").text( hasJson ? joinedJson.joinedAsn : '');
$("#dagrank_fld").text( hasJson ? dagrankJson.myDAGrank : '');
$("#outread_fld").text( hasJson ? outbufJson.index_read : '');
$("#outwrite_fld").text( hasJson ? outbufJson.index_write : '');
diff --git a/nativeSetup.py b/nativeSetup.py
index 3f60cb8a..3c7158ed 100644
--- a/nativeSetup.py
+++ b/nativeSetup.py
@@ -4,20 +4,20 @@
from openvisualizer import appdirs
'''
-This implementation of the traditional setup.py uses the application-level
-data_files parameter to store data files, rather than the package-level
+This implementation of the traditional setup.py uses the application-level
+data_files parameter to store data files, rather than the package-level
package_data parameter. We store these data files in operating system specific
, i.e. "native", locations with the help of the appdirs utility. For example,
shared data files on Linux are placed in "/usr/local/share/openvisualizer".
We use the site-level data and config directories because we expect the
-superuser to run OpenVisualizer, so user-level directories like
+superuser to run OpenVisualizer, so user-level directories like
"/home/
/.config" are not available.
-For native file storage to work, the installer *must not* modify the location
+For native file storage to work, the installer *must not* modify the location
of these files at install time.
-Use of the legacy distutils package also accommodates existing Linux packaging
+Use of the legacy distutils package also accommodates existing Linux packaging
tools.
'''
@@ -36,26 +36,26 @@ def appdirGlob(globstr, subdir=''):
return glob.glob('/'.join([appdir, globstr]))
else:
return glob.glob('/'.join([appdir, subdir, globstr]))
-
+
setup(
name = 'openVisualizer',
- packages = ['openvisualizer',
- 'openvisualizer.BspEmulator', 'openvisualizer.eventBus',
- 'openvisualizer.lbrClient', 'openvisualizer.moteConnector',
- 'openvisualizer.moteProbe', 'openvisualizer.moteState',
- 'openvisualizer.openLbr', 'openvisualizer.openTun',
- 'openvisualizer.openType', 'openvisualizer.openUI',
- 'openvisualizer.RPL', 'openvisualizer.SimEngine',
- 'openvisualizer.JRC'],
+ packages = ['openvisualizer',
+ 'openvisualizer.BspEmulator', 'openvisualizer.eventBus',
+ 'openvisualizer.lbrClient', 'openvisualizer.moteConnector',
+ 'openvisualizer.moteProbe', 'openvisualizer.moteState',
+ 'openvisualizer.openLbr', 'openvisualizer.openTun',
+ 'openvisualizer.openType', 'openvisualizer.openUI',
+ 'openvisualizer.RPL', 'openvisualizer.SimEngine',
+ 'openvisualizer.JRC', 'openvisualizer.openBenchmarkAgent'],
package_dir = {'': '.', 'openvisualizer': 'openvisualizer'},
scripts = appdirGlob('openVisualizer*.py'),
# Copy simdata files by extension so don't copy .gitignore in that directory.
data_files = [(confdir, appdirGlob('*.conf')),
- ('/'.join([datadir, webstatic, 'css']), appdirGlob('*', '/'.join([webstatic, 'css']))),
- ('/'.join([datadir, webstatic, 'font-awesome', 'css']), appdirGlob('*', '/'.join([webstatic, 'font-awesome', 'css']))),
- ('/'.join([datadir, webstatic, 'font-awesome', 'fonts']), appdirGlob('*', '/'.join([webstatic, 'font-awesome', 'fonts']))),
- ('/'.join([datadir, webstatic, 'images']), appdirGlob('*', '/'.join([webstatic, 'images']))),
+ ('/'.join([datadir, webstatic, 'css']), appdirGlob('*', '/'.join([webstatic, 'css']))),
+ ('/'.join([datadir, webstatic, 'font-awesome', 'css']), appdirGlob('*', '/'.join([webstatic, 'font-awesome', 'css']))),
+ ('/'.join([datadir, webstatic, 'font-awesome', 'fonts']), appdirGlob('*', '/'.join([webstatic, 'font-awesome', 'fonts']))),
+ ('/'.join([datadir, webstatic, 'images']), appdirGlob('*', '/'.join([webstatic, 'images']))),
('/'.join([datadir, webstatic, 'js']), appdirGlob('*.js', '/'.join([webstatic, 'js']))),
('/'.join([datadir, webstatic, 'js', 'plugins', 'metisMenu']), appdirGlob('*', '/'.join([webstatic, 'js', 'plugins', 'metisMenu']))),
('/'.join([datadir, webtmpl]), appdirGlob('*', webtmpl)),
diff --git a/openvisualizer/JRC/JRC.py b/openvisualizer/JRC/JRC.py
index 968990b1..670a4da4 100644
--- a/openvisualizer/JRC/JRC.py
+++ b/openvisualizer/JRC/JRC.py
@@ -23,13 +23,42 @@
import os
# ======================== Top Level JRC Class =============================
-class JRC():
- def __init__(self):
- coapResource = joinResource()
- self.coapServer = coapServer(coapResource, contextHandler(coapResource).securityContextLookup)
+class JRC(eventBusClient.eventBusClient):
+ def __init__(self, coapServer):
+ # store params
+ self.coapServer = coapServer
+
+ self.coapResource = joinResource()
+
+ self.coapServer.addResource(self.coapResource)
+ self.coapServer.addSecurityContextHandler(contextHandler(self.coapResource).securityContextLookup)
+
+ # initialize parent class
+ eventBusClient.eventBusClient.__init__(
+ self,
+ name='JRC',
+ registrations=[
+ {
+ 'sender': self.WILDCARD,
+ 'signal': 'getL2SecurityKey',
+ 'callback': self._getL2SecurityKey_notif,
+ },
+
+ ]
+ )
+
+ # ======================== public ==========================================
def close(self):
- self.coapServer.close()
+ pass
+
+ # ==== handle EventBus notifications
+
+ def _getL2SecurityKey_notif(self, sender, signal, data):
+ '''
+ Return L2 security key for the network.
+ '''
+ return {'index': [self.coapResource.networkKeyIndex], 'value': self.coapResource.networkKey}
# ======================== Security Context Handler =========================
class contextHandler():
@@ -66,184 +95,6 @@ def securityContextLookup(self, kid):
return context
-# ======================== Interface with OpenVisualizer ======================================
-class coapServer(eventBusClient.eventBusClient):
- # link-local prefix
- LINK_LOCAL_PREFIX = [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
-
- def __init__(self, coapResource, contextHandler=None):
- # log
- log.info("create instance")
-
- self.coapResource = coapResource
-
- # run CoAP server in testing mode
- # this mode does not open a real socket, rather uses PyDispatcher for sending/receiving messages
- # We interface this mode with OpenVisualizer to run JRC co-located with the DAG root
- self.coapServer = coap.coap(udpPort=d.DEFAULT_UDP_PORT, testing=True)
- self.coapServer.addResource(coapResource)
- self.coapServer.addSecurityContextHandler(contextHandler)
- self.coapServer.maxRetransmit = 1
-
- self.coapClient = None
-
- self.dagRootEui64 = None
-
- # store params
-
- # initialize parent class
- eventBusClient.eventBusClient.__init__(
- self,
- name='JRC',
- registrations=[
- {
- 'sender': self.WILDCARD,
- 'signal': 'getL2SecurityKey',
- 'callback': self._getL2SecurityKey_notif,
- },
- {
- 'sender': self.WILDCARD,
- 'signal': 'registerDagRoot',
- 'callback': self._registerDagRoot_notif
- },
- {
- 'sender': self.WILDCARD,
- 'signal': 'unregisterDagRoot',
- 'callback': self._unregisterDagRoot_notif
- },
- ]
- )
-
- # local variables
- self.stateLock = threading.Lock()
-
- # ======================== public ==========================================
-
- def close(self):
- # nothing to do
- pass
-
- # ======================== private =========================================
-
- # ==== handle EventBus notifications
-
- def _getL2SecurityKey_notif(self, sender, signal, data):
- '''
- Return L2 security key for the network.
- '''
- return {'index' : [self.coapResource.networkKeyIndex], 'value' : self.coapResource.networkKey}
-
- def _registerDagRoot_notif(self, sender, signal, data):
- # register for the global address of the DAG root
- self.register(
- sender=self.WILDCARD,
- signal=(
- tuple(data['prefix'] + data['host']),
- self.PROTO_UDP,
- d.DEFAULT_UDP_PORT
- ),
- callback=self._receiveFromMesh,
- )
-
- # register to receive at link-local DAG root's address
- self.register(
- sender=self.WILDCARD,
- signal=(
- tuple(self.LINK_LOCAL_PREFIX + data['host']),
- self.PROTO_UDP,
- d.DEFAULT_UDP_PORT
- ),
- callback=self._receiveFromMesh,
- )
-
- self.dagRootEui64 = data['host']
-
- def _unregisterDagRoot_notif(self, sender, signal, data):
- # unregister global address
- self.unregister(
- sender=self.WILDCARD,
- signal=(
- tuple(data['prefix'] + data['host']),
- self.PROTO_UDP,
- d.DEFAULT_UDP_PORT
- ),
- callback=self._receiveFromMesh,
- )
- # unregister link-local address
- self.unregister(
- sender=self.WILDCARD,
- signal=(
- tuple(self.LINK_LOCAL_PREFIX + data['host']),
- self.PROTO_UDP,
- d.DEFAULT_UDP_PORT
- ),
- callback=self._receiveFromMesh,
- )
-
- self.dagRootEui64 = None
-
- def _receiveFromMesh(self, sender, signal, data):
- '''
- Receive packet from the mesh destined for JRC's CoAP server.
- Forwards the packet to the virtual CoAP server running in test mode (PyDispatcher).
- '''
- sender = openvisualizer.openvisualizer_utils.formatIPv6Addr(data[0])
- # FIXME pass source port within the signal and open coap client at this port
- self.coapClient = coap.coap(ipAddress=sender, udpPort=d.DEFAULT_UDP_PORT, testing=True, receiveCallback=self._receiveFromCoAP)
- self.coapClient.socketUdp.sendUdp(destIp='', destPort=d.DEFAULT_UDP_PORT, msg=data[1]) # low level forward of the CoAP message
- return True
-
- def _receiveFromCoAP(self, timestamp, sender, data):
- '''
- Receive CoAP response and forward it to the mesh network.
- Appends UDP and IPv6 headers to the CoAP message and forwards it on the Eventbus towards the mesh.
- '''
- self.coapClient.close()
-
- # UDP
- udplen = len(data) + 8
-
- udp = u.int2buf(sender[1], 2) # src port
- udp += u.int2buf(self.coapClient.udpPort, 2) # dest port
- udp += [udplen >> 8, udplen & 0xff] # length
- udp += [0x00, 0x00] # checksum
- udp += data
-
- # destination address of the packet is CoAP client's IPv6 address (address of the mote)
- dstIpv6Address = u.ipv6AddrString2Bytes(self.coapClient.ipAddress)
- assert len(dstIpv6Address)==16
- # source address of the packet is DAG root's IPV6 address
- # use the same prefix (link-local or global) as in the destination address
- srcIpv6Address = dstIpv6Address[:8]
- srcIpv6Address += self.dagRootEui64
- assert len(srcIpv6Address)==16
-
- # CRC See https://tools.ietf.org/html/rfc2460.
-
- udp[6:8] = openvisualizer.openvisualizer_utils.calculatePseudoHeaderCRC(
- src=srcIpv6Address,
- dst=dstIpv6Address,
- length=[0x00, 0x00] + udp[4:6],
- nh=[0x00, 0x00, 0x00, 17], # UDP as next header
- payload=udp,
- )
-
- # IPv6
- ip = [6 << 4] # v6 + traffic class (upper nybble)
- ip += [0x00, 0x00, 0x00] # traffic class (lower nibble) + flow label
- ip += udp[4:6] # payload length
- ip += [17] # next header (protocol); UDP=17
- ip += [64] # hop limit (pick a safe value)
- ip += srcIpv6Address # source
- ip += dstIpv6Address # destination
- ip += udp
-
- # announce network prefix
- self.dispatch(
- signal = 'v6ToMesh',
- data = ip
- )
-
# ==================== Implementation of CoAP join resource =====================
class joinResource(coapResource.coapResource):
def __init__(self):
@@ -260,7 +111,7 @@ def __init__(self):
self.addSecurityBinding((None, [d.METHOD_POST])) # security context should be returned by the callback
- def POST(self,options=[], payload=[]):
+ def POST(self,options=[], payload=[], metaData={}):
link_layer_keyset = [self.networkKeyIndex, u.buf2str(self.networkKey)]
diff --git a/openvisualizer/coapServer/__init__.py b/openvisualizer/coapServer/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/openvisualizer/coapServer/coapServer.py b/openvisualizer/coapServer/coapServer.py
new file mode 100644
index 00000000..235700f9
--- /dev/null
+++ b/openvisualizer/coapServer/coapServer.py
@@ -0,0 +1,216 @@
+from coap import coap, \
+ coapResource, \
+ coapDefines as d, \
+ coapOption as o, \
+ coapUtils as u, \
+ coapObjectSecurity as oscoap, \
+ socketUdp
+import logging.handlers
+try:
+ from openvisualizer.eventBus import eventBusClient
+ import openvisualizer.openvisualizer_utils
+except ImportError:
+ pass
+
+log = logging.getLogger('coapServer')
+log.setLevel(logging.ERROR)
+log.addHandler(logging.NullHandler())
+
+import cbor
+import binascii
+import os
+import time
+import threading
+
+# default IPv6 hop limit
+COAP_SERVER_DEFAULT_IPv6_HOP_LIMIT = 65
+
+class coapDispatcher(socketUdp.socketUdp, eventBusClient.eventBusClient):
+
+ # link-local prefix
+ LINK_LOCAL_PREFIX = [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+
+ def __init__(self, ipAddress, udpPort, callback):
+ # log
+ log.debug('creating instance')
+
+ # params
+ self.udpPort = udpPort
+ self.dagRootEui64 = None
+ self.networkPrefix = None
+ self.callback = callback
+
+ # initialize the parent socketUdp class
+ socketUdp.socketUdp.__init__(self, ipAddress, self.udpPort, self.callback)
+
+ # initialize the parent class eventBusClient class
+ eventBusClient.eventBusClient.__init__(
+ self,
+ name='coapDispatcher',
+ registrations=[
+ {
+ 'sender': self.WILDCARD,
+ 'signal': 'registerDagRoot',
+ 'callback': self._registerDagRoot_notif
+ },
+ {
+ 'sender': self.WILDCARD,
+ 'signal': 'unregisterDagRoot',
+ 'callback': self._unregisterDagRoot_notif
+ },
+ ]
+ )
+
+ # change name
+ self.name = 'coapDispatcher@DagRootIpv6:{0}'.format(self.udpPort)
+ self.gotMsgSem = threading.Semaphore()
+
+ # start myself
+ self.start()
+
+ # ======================== public ==========================================
+ # TODO rework the class for it to be completely stateless and not depend on self.dagRootEui64
+ def sendUdp(self, destIp, destPort, msg):
+ '''
+ Receive CoAP response and forward it to the mesh network.
+ Appends UDP and IPv6 headers to the CoAP message and forwards it on the Eventbus towards the mesh.
+ '''
+
+ assert self.dagRootEui64
+ assert self.networkPrefix
+
+ log.debug("sendUdp to {0}:{1} message {2}".format(destIp,destPort,msg))
+
+ # UDP
+ udplen = len(msg) + 8
+
+ # FIXME need to signal the source port from the packet
+ udp = u.int2buf(self.udpPort, 2) # src port
+ udp += u.int2buf(destPort, 2) # dest port
+ udp += [udplen >> 8, udplen & 0xff] # length
+ udp += [0x00, 0x00] # checksum
+ udp += msg
+
+ # destination address of the packet is CoAP client's IPv6 address (address of the mote)
+ dstIpv6Address = u.ipv6AddrString2Bytes(destIp)
+ assert len(dstIpv6Address) == 16
+ # source address of the packet is DAG root's IPV6 address
+ # use the same prefix (link-local or global) as in the destination address
+ srcIpv6Address = dstIpv6Address[:8]
+ srcIpv6Address += self.dagRootEui64
+ assert len(srcIpv6Address) == 16
+
+ # CRC See https://tools.ietf.org/html/rfc2460.
+
+ udp[6:8] = openvisualizer.openvisualizer_utils.calculatePseudoHeaderCRC(
+ src=srcIpv6Address,
+ dst=dstIpv6Address,
+ length=[0x00, 0x00] + udp[4:6],
+ nh=[0x00, 0x00, 0x00, 17], # UDP as next header
+ payload=udp,
+ )
+
+ # IPv6
+ ip = [6 << 4] # v6 + traffic class (upper nybble)
+ ip += [0x00, 0x00, 0x00] # traffic class (lower nibble) + flow label
+ ip += udp[4:6] # payload length
+ ip += [17] # next header (protocol); UDP=17
+ ip += [COAP_SERVER_DEFAULT_IPv6_HOP_LIMIT] # hop limit (pick a safe value)
+ ip += srcIpv6Address # source
+ ip += dstIpv6Address # destination
+ ip += udp
+
+ self.dispatch(
+ signal='v6ToMesh',
+ data=ip
+ )
+
+ # update stats
+ self._incrementTx()
+
+ def close(self):
+ # stop
+ self.goOn = False
+ self.gotMsgSem.release()
+
+ # ======================== private =========================================
+
+ def _messageNotification(self, sender, signal, data):
+ # log
+ log.debug("messageNotification: got {1} from {0}".format(sender, data))
+
+ srcIpv6 = openvisualizer.openvisualizer_utils.formatIPv6Addr(data[0])
+ rawbytes = data[1]
+ hopLimit = data[2] # IPv6 metadata
+ timestamp = str(data[3]) # timestamp of the received packet
+
+ sender = (srcIpv6, self.udpPort, (hopLimit, timestamp))
+
+ # call the callback
+ self.callback(timestamp, sender, rawbytes)
+
+ # update stats
+ self._incrementRx()
+
+ # release the lock
+ self.gotMsgSem.release()
+
+ # return success in order to acknowledge the reception
+ return True
+
+ def run(self):
+ while self.goOn:
+ self.gotMsgSem.acquire()
+
+ # ==== handle EventBus notifications
+
+ def _registerDagRoot_notif(self, sender, signal, data):
+ # register for the global address of the DAG root
+ self.register(
+ sender=self.WILDCARD,
+ signal=(
+ tuple(data['prefix'] + data['host']),
+ self.PROTO_UDP,
+ d.DEFAULT_UDP_PORT
+ ),
+ callback=self._messageNotification,
+ )
+
+ # register to receive at link-local DAG root's address
+ self.register(
+ sender=self.WILDCARD,
+ signal=(
+ tuple(self.LINK_LOCAL_PREFIX + data['host']),
+ self.PROTO_UDP,
+ d.DEFAULT_UDP_PORT
+ ),
+ callback=self._messageNotification,
+ )
+
+ self.dagRootEui64 = data['host']
+ self.networkPrefix = data['prefix']
+
+ def _unregisterDagRoot_notif(self, sender, signal, data):
+ # unregister global address
+ self.unregister(
+ sender=self.WILDCARD,
+ signal=(
+ tuple(data['prefix'] + data['host']),
+ self.PROTO_UDP,
+ d.DEFAULT_UDP_PORT
+ ),
+ callback=self._messageNotification,
+ )
+ # unregister link-local address
+ self.unregister(
+ sender=self.WILDCARD,
+ signal=(
+ tuple(self.LINK_LOCAL_PREFIX + data['host']),
+ self.PROTO_UDP,
+ d.DEFAULT_UDP_PORT
+ ),
+ callback=self._messageNotification,
+ )
+
+ self.dagRootEui64 = None
+ self.networkPrefix = None
diff --git a/openvisualizer/eventBus/eventBusMonitor.py b/openvisualizer/eventBus/eventBusMonitor.py
index 7e2b8b35..25375ac5 100644
--- a/openvisualizer/eventBus/eventBusMonitor.py
+++ b/openvisualizer/eventBus/eventBusMonitor.py
@@ -132,7 +132,7 @@ def _eventBusNotification(self,signal,sender,data):
if signal=='fromMote.data':
# Forwards a copy of the data received from a mode
# to the Internet interface for debugging.
- (previousHop,lowpan) = data
+ (previousHop,lowpan,timestamp) = data
zep = self._wrapMacAndZep(
previousHop = previousHop,
diff --git a/openvisualizer/moteConnector/OpenParser.py b/openvisualizer/moteConnector/OpenParser.py
index cf85976f..9886e122 100644
--- a/openvisualizer/moteConnector/OpenParser.py
+++ b/openvisualizer/moteConnector/OpenParser.py
@@ -15,6 +15,7 @@
import ParserData
import ParserPacket
import ParserPrintf
+import ParserBenchmark
class OpenParser(Parser.Parser):
@@ -27,6 +28,7 @@ class OpenParser(Parser.Parser):
SERFRAME_MOTE2PC_CRITICAL = ParserIEC.ParserInfoErrorCritical.SEVERITY_CRITICAL
SERFRAME_MOTE2PC_SNIFFED_PACKET = ord('P')
SERFRAME_MOTE2PC_PRINTF = ord('F')
+ SERFRAME_MOTE2PC_BENCHMARK = ord('B')
SERFRAME_PC2MOTE_SETDAGROOT = ord('R')
SERFRAME_PC2MOTE_DATA = ord('D')
@@ -53,6 +55,7 @@ def __init__(self, mqtt_broker_address):
self.parserData = ParserData.ParserData(mqtt_broker_address)
self.parserPacket = ParserPacket.ParserPacket()
self.parserPrintf = ParserPrintf.ParserPrintf()
+ self.parserBenchmark = ParserBenchmark.ParserBenchmark()
# register subparsers
self._addSubParser(
@@ -90,6 +93,11 @@ def __init__(self, mqtt_broker_address):
val = self.SERFRAME_MOTE2PC_PRINTF,
parser = self.parserPrintf.parseInput,
)
+ self._addSubParser(
+ index = 0,
+ val = self.SERFRAME_MOTE2PC_BENCHMARK,
+ parser = self.parserBenchmark.parseInput,
+ )
#======================== public ==========================================
diff --git a/openvisualizer/moteConnector/ParserBenchmark.py b/openvisualizer/moteConnector/ParserBenchmark.py
new file mode 100644
index 00000000..b8996fcd
--- /dev/null
+++ b/openvisualizer/moteConnector/ParserBenchmark.py
@@ -0,0 +1,63 @@
+# Copyright (c) 2010-2013, Regents of the University of California.
+# All rights reserved.
+#
+# Released under the BSD 3-Clause license as published at the link below.
+# https://openwsn.atlassian.net/wiki/display/OW/License
+import logging
+
+log = logging.getLogger('ParserBenchmark')
+log.setLevel(logging.ERROR)
+log.addHandler(logging.NullHandler())
+
+import struct
+
+from pydispatch import dispatcher
+
+from ParserException import ParserException
+import Parser
+
+from openvisualizer.openType import typeAsn
+from openvisualizer import openvisualizer_utils as u
+
+class ParserBenchmark(Parser.Parser):
+ HEADER_LENGTH = 2
+
+ def __init__(self):
+
+ # log
+ log.info("create instance")
+
+ # initialize parent class
+ Parser.Parser.__init__(self, self.HEADER_LENGTH)
+
+ self._asn = ['asn_4', # B
+ 'asn_2_3', # H
+ 'asn_0_1', # H
+ ]
+
+ # ======================== public ==========================================
+
+ def parseInput(self, input):
+ # log
+ log.debug("received data {0}".format(input))
+
+ # ensure input not short longer than header
+ self._checkLength(input)
+
+ source = input[:8]
+ source = u.formatAddr(source)
+
+ event = input[8]
+
+ asnParsed = struct.unpack(' 6pList tx, 5, 3"
print "comma. e.g. set 6pClear all"
return [outcome,dataToSend]
- elif data[0] == 'joinKey':
+ elif commandName == 'joinKey':
try:
- if len(data[1]) != commandLen*2: # two hex chars is one byte
+ if len(parameter) != commandLen*2: # two hex chars is one byte
raise ValueError
- payload = binascii.unhexlify(data[1])
+ payload = binascii.unhexlify(parameter)
dataToSend = [OpenParser.OpenParser.SERFRAME_PC2MOTE_COMMAND,
commandId,
commandLen,
@@ -294,8 +297,29 @@ def _commandToBytes(self,data):
except:
print "============================================="
print "Wrong joinKey format. Input 16-byte long hex string. e.g. cafebeefcafebeefcafebeefcafebeef"
+ elif commandName == 'sendPacket':
+ try:
+
+ if len(parameter) != commandLen:
+ raise ValueError("Invalid sendPacket payload, expecting {0} bytes".format(commandLen))
+
+ dataToSend = [OpenParser.OpenParser.SERFRAME_PC2MOTE_COMMAND,
+ commandId,
+ commandLen,
+ ]
+ dataToSend += parameter
+ except:
+ debug = "=============================================\n"
+ debug += "Wrong sendPacket command format.\n"
+ debug += "Supported: ( destination, confirmable, packetsInBurst, packetToken, packetPayloadLen )\n"
+ debug += "destination: dash-separated EUI-64 string, e.g. AA-BB-CC-DD-EE-FF-00-11\n"
+ debug += "confirmable: boolean\n"
+ debug += "packetsInBurst: integer\n"
+ debug += "packetToken: integer array\n"
+ debug += "packetPayloadLen: integer\n"
+ log.warning(debug)
else:
- parameter = int(data[1])
+ parameter = int(parameter)
if parameter <= 0xffff:
parameter = [(parameter & 0xff),((parameter >> 8) & 0xff)]
dataToSend = [OpenParser.OpenParser.SERFRAME_PC2MOTE_COMMAND,
@@ -315,7 +339,6 @@ def _commandToBytes(self,data):
outcome = True
return [outcome,dataToSend]
-
def _bytesToMesh_handler(self,sender,signal,data):
assert type(data)==tuple
assert len(data)==2
diff --git a/openvisualizer/moteProbe/OpenHdlc.py b/openvisualizer/moteProbe/OpenHdlc.py
index f581aebd..b325830b 100644
--- a/openvisualizer/moteProbe/OpenHdlc.py
+++ b/openvisualizer/moteProbe/OpenHdlc.py
@@ -95,8 +95,8 @@ def dehdlcify(self,inBuf):
:returns: the extracted frame, or -1 if wrong checksum
'''
- assert inBuf[ 0]==self.HDLC_FLAG
- assert inBuf[-1]==self.HDLC_FLAG
+ if inBuf[0]!=self.HDLC_FLAG or inBuf[-1]!=self.HDLC_FLAG:
+ raise HdlcException('unexpected frame')
# make copy of input
outBuf = inBuf[:]
diff --git a/openvisualizer/moteProbe/moteProbe.py b/openvisualizer/moteProbe/moteProbe.py
index da7231cf..1b9b2766 100644
--- a/openvisualizer/moteProbe/moteProbe.py
+++ b/openvisualizer/moteProbe/moteProbe.py
@@ -103,9 +103,10 @@ class OpentestbedMoteFinder (object):
OPENTESTBED_RESP_STATUS_TIMEOUT = 10
- def __init__(self, mqtt_broker_address):
- self.opentestbed_motelist = set()
+ def __init__(self, testbed, mqtt_broker_address):
+ self.testbed = testbed
self.mqtt_broker_address = mqtt_broker_address
+ self.opentestbed_motelist = set()
def get_opentestbed_motelist(self):
@@ -130,14 +131,14 @@ def _on_mqtt_connect(self, client, userdata, flags, rc):
print "connected to : {0}".format(self.mqtt_broker_address)
- client.subscribe('opentestbed/deviceType/box/deviceId/+/resp/status')
+ client.subscribe('{0}/deviceType/box/deviceId/+/resp/status'.format(self.testbed))
payload_status = {
'token': 123,
}
# publish the cmd message
client.publish(
- topic = 'opentestbed/deviceType/box/deviceId/all/cmd/status',
+ topic = '{0}/deviceType/box/deviceId/all/cmd/status'.format(self.testbed),
payload = json.dumps(payload_status),
)
@@ -146,10 +147,19 @@ def _on_mqtt_message(self, client, userdata, message):
# get the motes list from payload
payload_status = json.loads(message.payload)
+
+ try:
+ host = payload_status['returnVal']['host_name']
+ except KeyError:
+ host = payload_status['returnVal']['IP_address']
+ except:
+ host = ''
for mote in payload_status['returnVal']['motes']:
if 'EUI64' in mote:
- self.opentestbed_motelist.add(mote['EUI64'])
+ self.opentestbed_motelist.add(
+ (host, mote['EUI64'], self.testbed, self.mqtt_broker_address)
+ )
class moteProbe(threading.Thread):
@@ -163,7 +173,7 @@ class moteProbe(threading.Thread):
MODE_IOTLAB,
MODE_TESTBED,
]
-
+
XOFF = 0x13
XON = 0x11
XONXOFF_ESCAPE = 0x12
@@ -172,25 +182,25 @@ class moteProbe(threading.Thread):
# XON is transmitted as [XONXOFF_ESCAPE, XON^XONXOFF_MASK]==[0x12,0x11^0x10]==[0x12,0x01]
# XONXOFF_ESCAPE is transmitted as [XONXOFF_ESCAPE, XONXOFF_ESCAPE^XONXOFF_MASK]==[0x12,0x12^0x10]==[0x12,0x02]
- def __init__(self,mqtt_broker_address,serialport=None,emulatedMote=None,iotlabmote=None,testbedmote_eui64=None):
+ def __init__(self,mqtt_broker_address,serialport=None,emulatedMote=None,iotlabmote=None,testbedmote=None):
# verify params
if serialport:
assert not emulatedMote
assert not iotlabmote
- assert not testbedmote_eui64
+ assert not testbedmote
self.mode = self.MODE_SERIAL
elif emulatedMote:
assert not serialport
assert not iotlabmote
- assert not testbedmote_eui64
+ assert not testbedmote
self.mode = self.MODE_EMULATED
elif iotlabmote:
assert not serialport
assert not emulatedMote
- assert not testbedmote_eui64
+ assert not testbedmote
self.mode = self.MODE_IOTLAB
- elif testbedmote_eui64:
+ elif testbedmote:
assert not serialport
assert not emulatedMote
assert not iotlabmote
@@ -210,8 +220,8 @@ def __init__(self,mqtt_broker_address,serialport=None,emulatedMote=None,iotlabmo
self.iotlabmote = iotlabmote
self.portname = 'IoT-LAB{0}'.format(iotlabmote)
elif self.mode==self.MODE_TESTBED:
- self.testbedmote_eui64 = testbedmote_eui64
- self.portname = 'opentestbed_{0}'.format(testbedmote_eui64)
+ (self.testbed_host, self.testbedmote_eui64, self.testbed, self.mqtt_broker_address) = testbedmote
+ self.portname = 'testbed_{0}_{1}_{2}'.format(self.testbed, self.testbed_host, self.testbedmote_eui64)
else:
raise SystemError()
# at this moment, MQTT broker is used even if the mode is not
@@ -409,7 +419,7 @@ def _sendData(self,data):
payload_buffer['serialbytes'] = [ord(i) for i in hdlcData]
# publish the cmd message
self.mqttclient.publish(
- topic = 'opentestbed/deviceType/mote/deviceId/{0}/cmd/tomoteserialbytes'.format(self.testbedmote_eui64),
+ topic = '{0}/deviceType/mote/deviceId/{1}/cmd/tomoteserialbytes'.format(self.testbed, self.testbedmote_eui64),
payload = json.dumps(payload_buffer),
)
else:
@@ -420,7 +430,7 @@ def _sendData(self,data):
def _on_mqtt_connect(self, client, userdata, flags, rc):
- client.subscribe('opentestbed/deviceType/mote/deviceId/{0}/notif/frommoteserialbytes'.format(self.testbedmote_eui64))
+ client.subscribe('{0}/deviceType/mote/deviceId/{1}/notif/frommoteserialbytes'.format(self.testbed, self.testbedmote_eui64))
def _on_mqtt_message(self, client, userdata, message):
diff --git a/openvisualizer/moteState/moteState.py b/openvisualizer/moteState/moteState.py
index c1d675bc..2cc13b32 100644
--- a/openvisualizer/moteState/moteState.py
+++ b/openvisualizer/moteState/moteState.py
@@ -13,6 +13,7 @@
log.setLevel(logging.ERROR)
log.addHandler(logging.NullHandler())
+
import copy
import time
import threading
@@ -27,6 +28,8 @@
typeComponent, \
typeRssi
+from openvisualizer import openvisualizer_utils as u
+
class OpenEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (StateElem,openType.openType)):
@@ -121,7 +124,8 @@ def _elemToDict(self,elem):
class StateOutputBuffer(StateElem):
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
if len(self.data)==0:
self.data.append({})
@@ -130,7 +134,8 @@ def update(self,notif):
class StateAsn(StateElem):
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
if len(self.data)==0:
self.data.append({})
@@ -139,21 +144,16 @@ def update(self,notif):
self.data[0]['asn'].update(notif.asn_0_1,
notif.asn_2_3,
notif.asn_4)
-class StateJoined(StateElem):
-
- def update(self,notif):
- StateElem.update(self)
- if len(self.data)==0:
- self.data.append({})
- if 'joinedAsn' not in self.data[0]:
- self.data[0]['joinedAsn'] = typeAsn.typeAsn()
- self.data[0]['joinedAsn'].update(notif.joinedAsn_0_1,
- notif.joinedAsn_2_3,
- notif.joinedAsn_4)
+
+ def getAsn(self):
+ if len(self.data) != 0:
+ return self.data[0]['asn']
+ return None
class StateMacStats(StateElem):
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
if len(self.data)==0:
self.data.append({})
@@ -168,9 +168,15 @@ def update(self,notif):
else:
self.data[0]['dutyCycle'] = '?'
+ def getDutyCycle(self):
+ if len(self.data)!=0:
+ return self.data[0]['dutyCycle']
+ return None
+
class StateScheduleRow(StateElem):
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
if len(self.data)==0:
self.data.append({})
@@ -194,9 +200,13 @@ def update(self,notif):
notif.lastUsedAsn_2_3,
notif.lastUsedAsn_4)
+ def getType(self):
+ return self.data[0]['type']
+
class StateBackoff(StateElem):
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
if len(self.data)==0:
self.data.append({})
@@ -225,7 +235,8 @@ def __init__(self):
for i in range(20):
self.data.append(StateQueueRow())
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
self.data[0].update(notif.creator_0,notif.owner_0)
self.data[1].update(notif.creator_1,notif.owner_1)
@@ -250,7 +261,8 @@ def update(self,notif):
class StateNeighborsRow(StateElem):
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
if len(self.data)==0:
self.data.append({})
@@ -285,7 +297,8 @@ def update(self,notif):
class StateIsSync(StateElem):
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
if len(self.data)==0:
self.data.append({})
@@ -303,9 +316,28 @@ def get16bAddr(self):
try:
return self.data[0]['my16bID'].addr[:]
except IndexError:
- return None
-
- def update(self,notif):
+ return []
+
+ def get64bAddr(self):
+ try:
+ return self.data[0]['my64bID'].addr[:]
+ except IndexError:
+ return []
+
+ def get_serial(self):
+ return self.moteConnector.serialport
+
+ def get_info(self):
+ return {
+ '64bAddr' : u.formatAddr(self.get64bAddr()),
+ '16bAddr' : u.formatAddr(self.get16bAddr()),
+ 'isDAGroot' : self.isDAGroot,
+ 'serial' : self.get_serial(),
+ }
+
+ def update(self,data):
+
+ (moteInfo, notif) = data
# update state
StateElem.update(self)
@@ -376,7 +408,8 @@ def update(self,notif):
class StateMyDagRank(StateElem):
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
if len(self.data)==0:
self.data.append({})
@@ -384,12 +417,14 @@ def update(self,notif):
class StatekaPeriod(StateElem):
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
if len(self.data)==0:
self.data.append({})
self.data[0]['kaPeriod'] = notif.kaPeriod
+# abstract class
class StateTable(StateElem):
def __init__(self,rowClass,columnOrder=None):
@@ -399,29 +434,58 @@ def __init__(self,rowClass,columnOrder=None):
self.meta[0]['columnOrder'] = columnOrder
self.data = []
- def update(self,notif):
+ def update(self,data):
+ (moteInfo, notif) = data
StateElem.update(self)
while len(self.data) 10:
+ self.outstandingPacketsFromDagRoot.pop(0)
+ self.outstandingPacketsFromDagRoot.append(packet)
+
+ # ======================== private =========================================
+
+ # packetSent
+ def _handler_event_packetSent(self, buf):
+ returnVal = {}
+
+ packetToken = buf[:5]
+ destBuf = buf[5:13]
+ dest = openvisualizer.openvisualizer_utils.formatAddr(destBuf)
+ hopLimit = buf[13]
+
+ returnVal['packetToken'] = packetToken
+ returnVal['destination'] = dest
+ returnVal['hopLimit'] = hopLimit
+
+ return (True, returnVal)
+
+ def _handler_event_packetSentDagRoot(self, buf):
+ receivedToken = buf
+ newBuf = []
+
+ for packet in self.outstandingPacketsFromDagRoot:
+ (token, destination, hopLimit) = packet
+ if receivedToken == token:
+ # we have a hit, remove it from outstanding packets
+ log.debug("packetSentDagRoot event: hit for packet {0}".format(packet))
+ self.outstandingPacketsFromDagRoot.remove(packet)
+
+ # construct the missing fields from the saved values
+ newBuf += token
+ newBuf += destination
+ newBuf += [hopLimit]
+ return self._handler_event_packetSent(newBuf)
+ # not found, not all packets sent by dag root are originated by openbenchmark: ignore it
+ log.debug("packetSentDagRoot event: miss for token {0}".format(receivedToken))
+ return (False, {})
+
+ # packetReceived, same syntax as packetSent
+ def _handler_event_packetReceived(self, buf):
+ return self._handler_event_packetSent(buf)
+
+ # synchronizationCompleted
+ def _handler_event_synchronizationCompleted(self, buf):
+ return (True, {})
+
+ # desynchronized
+ def _handler_event_desynchronized(self, buf):
+ return (True, {})
+
+ # secureJoinCompleted
+ def _handler_event_secureJoinCompleted(self, buf):
+ return (True, {})
+
+ # bandwidthAssigned
+ def _handler_event_bandwidthAssigned(self, buf):
+ return (True, {})
+
+ # networkFormationCompleted
+ def _handler_event_networkFormationCompleted(self, buf):
+ # TODO
+ return (True, {})
+
+# ================= helper class that polls for periodic measurements =====================
+
+class PerformanceUpdatePoller(eventBusClient.eventBusClient, threading.Thread):
+
+ # periodic event names
+ EV_RADIO_DUTY_CYCLE_MEASUREMENT = ['radioDutyCycleMeasurement', 258 ]
+ EV_CLOCK_DRIFT_MEASUREMENT = ['clockDriftMeasurement', 259 ]
+
+ EV_SYNC_ALL = [
+ EV_RADIO_DUTY_CYCLE_MEASUREMENT,
+ EV_CLOCK_DRIFT_MEASUREMENT,
+ ]
+
+ def __init__(self, experimentId, mqttClient, period):
+ # log
+ log.info("creating a thread for periodic polling of performance metrics")
+
+ # params
+ self.experimentId = experimentId
+ self.mqttClient = mqttClient
+ self.period = period
+
+ # local vars
+ self.dutyCycleMeasurements = set()
+ self.dataLock = threading.Lock()
+
+ # flag to permit exit from infinite loop
+ self.goOn = True
+
+ # initialize the parent class
+ threading.Thread.__init__(self)
+
+ # give this thread a name
+ self.name = 'performanceUpdatePollerThread'
+
+ # subscribe to eventBus performance-related events
+ eventBusClient.eventBusClient.__init__(
+ self,
+ name='performanceUpdatePoller',
+ registrations=[
+ {
+ 'sender': self.WILDCARD,
+ 'signal': 'dutyCycleMeasurement',
+ 'callback': self.handle_dutyCycleMeasurement,
+ },
+ # TODO clock drift measurements
+ ]
+ )
+ # start myself
+ self.start()
+
+ # ======================== public =========================================
+
+ def run(self):
+ try:
+ # log
+ log.info("start running")
+
+ while self.goOn:
+
+ # poll moteState for latest measurements
+ self.dispatch('getDutyCycleMeasurement', [])
+
+ # wait for a while to gather the response from motes
+ time.sleep(1)
+
+ for measurement in self.dutyCycleMeasurements:
+
+ # dispatch each as an individual message
+ (source, timestamp, dutyCycle) = measurement
+
+ topic = 'openbenchmark/experimentId/{0}/nodeId/{1}/performanceData'.format(self.experimentId, source)
+ payload = {
+ 'event' : self.EV_RADIO_DUTY_CYCLE_MEASUREMENT[0],
+ 'timestamp' : int(timestamp,16),
+ 'source' : source,
+ 'dutyCycle' : dutyCycle,
+ }
+
+ log.debug("Publishing on topic: {0} Payload: {1}".format(topic, payload))
+
+ self.mqttClient.publish(
+ topic='openbenchmark/experimentId/{0}/nodeId/{0}/performanceData'.format(self.experimentId, source),
+ payload=json.dumps(payload),
+ )
+
+ with self.dataLock:
+ # reset for the next measurement
+ self.dutyCycleMeasurements = set()
+
+ time.sleep(self.period)
+
+ except Exception as err:
+ errMsg = openvisualizer.openvisualizer_utils.formatCrashMessage(self.name, err)
+ print errMsg
+ log.critical(errMsg)
+ self.close()
+ finally:
+ pass
+
+ def close(self):
+ self.goOn = False
+
+ def handle_dutyCycleMeasurement(self, sender, signal, data):
+ with self.dataLock:
+ self.dutyCycleMeasurements.add(
+ ( data['source'], data['timestamp'], data['dutyCycle'] )
+ )
+
+# ==================== Implementation of CoAP openbenchmark resource =====================
+
+class OpenbenchmarkResource(coapResource.coapResource):
+
+ def __init__(self, performanceEvent, dagRootEui64):
+
+ # params
+ self.performanceEvent = performanceEvent
+ self.dagRootEui64Buf = dagRootEui64
+
+ # initialize parent class
+ coapResource.coapResource.__init__(
+ self,
+ path='b',
+ )
+
+ def POST(self, options=[], payload=[], metaData={}):
+
+ assert self.dagRootEui64Buf
+
+ respPayload = []
+ respOptions = []
+
+ # token is the payload
+ token = payload
+ timestamp = metaData['generic_1']
+ source = openvisualizer.openvisualizer_utils.formatAddr(self.dagRootEui64Buf)
+
+ destinationBuf = u.ipv6AddrString2Bytes(metaData['srcIP'])[8:]
+ destinationEui64String = openvisualizer.openvisualizer_utils.formatAddr(destinationBuf)
+
+ log.debug("OpenbenchmarkResource: POST handler received metadata: {0}".format(metaData))
+
+ dict = {
+ 'packetToken' : token,
+ 'destination' : destinationEui64String,
+ 'hopLimit' : metaData['generic_0'],
+ }
+
+ self.performanceEvent.publish_event(PerformanceEvent.EV_PACKET_RECEIVED[2], timestamp, source, dict)
+
+ noResponse = False
+ for option in options:
+ if isinstance(option, o.NoResponse):
+ noResponse = True
+
+ # prepare the response
+ if not noResponse:
+ respPayload = token
+ respPayload[4] = (respPayload[4] + 1) % 255
+ self.performanceEvent.add_outstanding_packet(
+ (respPayload, destinationBuf, coapServer.COAP_SERVER_DEFAULT_IPv6_HOP_LIMIT)
+ )
+
+ return d.COAP_RC_2_04_CHANGED, respOptions, respPayload
diff --git a/openvisualizer/openLbr/openLbr.py b/openvisualizer/openLbr/openLbr.py
index a17439a4..64c7e44a 100644
--- a/openvisualizer/openLbr/openLbr.py
+++ b/openvisualizer/openLbr/openLbr.py
@@ -283,7 +283,11 @@ def _meshToV6_notif(self,sender,signal,data):
try:
ipv6dic={}
#build lowpan dictionary from the data
- ipv6dic = self.lowpan_to_ipv6(data)
+
+ source = data[0]
+ buf = data[1]
+ timestamp = data[2]
+ ipv6dic = self.lowpan_to_ipv6(source, buf)
success = True
dispatchSignal = None
@@ -302,7 +306,7 @@ def _meshToV6_notif(self,sender,signal,data):
#ipv6 header (inner)
ipv6dic_inner = {}
# parsing the iphc inner header and get the next_header
- ipv6dic_inner = self.lowpan_to_ipv6([ipv6dic['pre_hop'],ipv6dic['payload']])
+ ipv6dic_inner = self.lowpan_to_ipv6(ipv6dic['pre_hop'],ipv6dic['payload'])
ipv6dic['next_header'] = ipv6dic_inner['next_header']
ipv6dic['payload'] = ipv6dic_inner['payload']
ipv6dic['payload_length'] = ipv6dic_inner['payload_length']
@@ -416,7 +420,7 @@ def _meshToV6_notif(self,sender,signal,data):
#as source address is being retrieved from the IPHC header, the signal includes it in case
#receiver such as RPL DAO processing needs to know the source.
- success = self._dispatchProtocol(dispatchSignal,(ipv6dic['src_addr'],ipv6dic['app_payload']))
+ success = self._dispatchProtocol(dispatchSignal,(ipv6dic['src_addr'],ipv6dic['app_payload'],ipv6dic['hop_limit'],timestamp))
if success:
return
@@ -754,11 +758,9 @@ def reassemble_lowpan(self,lowpan):
#===== 6LoWPAN -> IPv6
- def lowpan_to_ipv6(self,data):
+ def lowpan_to_ipv6(self, mac_prev_hop, pkt_lowpan):
pkt_ipv6 = {}
- mac_prev_hop=data[0]
- pkt_lowpan=data[1]
if pkt_lowpan[0]==self.PAGE_ONE_DISPATCH:
ptr = 1
diff --git a/openvisualizer/openType/typeAsn.py b/openvisualizer/openType/typeAsn.py
index 5a268a47..1117433b 100644
--- a/openvisualizer/openType/typeAsn.py
+++ b/openvisualizer/openType/typeAsn.py
@@ -18,10 +18,24 @@ def __init__(self):
# initialize parent class
openType.openType.__init__(self)
+
+ self.asn = None
def __str__(self):
+ if self.asn is None:
+ return '?'
return '0x{0}'.format(''.join(["%.2x"%b for b in self.asn]))
-
+
+ def __eq__(self, other):
+ if isinstance(other, typeAsn):
+ return self.asn == other.asn
+ return False
+
+ def __ne__(self, other):
+ if isinstance(other, typeAsn):
+ return self.asn != other.asn
+ return True
+
#======================== public ==========================================
def update(self,byte0_1,byte2_3,byte4):
diff --git a/openvisualizer/openType/typeCellType.py b/openvisualizer/openType/typeCellType.py
index e3ac5d01..87b641b1 100644
--- a/openvisualizer/openType/typeCellType.py
+++ b/openvisualizer/openType/typeCellType.py
@@ -48,6 +48,9 @@ def update(self,type):
else:
self.desc = 'unknown'
self.addr = None
+
+ def getCellType(self):
+ return self.desc
#======================== private =========================================
\ No newline at end of file
diff --git a/openvisualizer/openvisualizer_utils.py b/openvisualizer/openvisualizer_utils.py
index 998d1d68..d13844f0 100644
--- a/openvisualizer/openvisualizer_utils.py
+++ b/openvisualizer/openvisualizer_utils.py
@@ -53,24 +53,32 @@ def formatThreadList():
#===== parsing
-def hex2buf(s):
+def hex2buf(s,separator=None):
'''
Convert a string of hex caracters into a byte list. For example:
``'abcdef00' -> [0xab,0xcd,0xef,0x00]``
:param s: [in] The string to convert
+ :param separator: [in] Optional separator char used in the string, e.g ab-cd-ef-00'
:returns: A list of integers, each element in [0x00..0xff].
'''
assert type(s)==str
- assert len(s)%2 == 0
+ if separator:
+ assert type(separator)==str
+ assert len(separator)==1
+ newVal = s.replace(separator,'') # remove the separator before proceding
+ else:
+ newVal = s
+
+ assert len(newVal)%2 == 0
returnVal = []
- for i in range(len(s)/2):
+ for i in range(len(newVal)/2):
realIdx = i*2
- returnVal.append(int(s[realIdx:realIdx+2],16))
+ returnVal.append(int(newVal[realIdx:realIdx+2],16))
return returnVal
diff --git a/setup.py b/setup.py
index 619ad78c..4cb5323c 100644
--- a/setup.py
+++ b/setup.py
@@ -7,9 +7,9 @@
from openvisualizer import ovVersion
'''
-This implementation of the traditional setup.py uses the root package's
-package_data parameter to store data files, rather than the application-level
-data_files parameter. This arrangement organizes OpenVisualizer within a
+This implementation of the traditional setup.py uses the root package's
+package_data parameter to store data files, rather than the application-level
+data_files parameter. This arrangement organizes OpenVisualizer within a
single tree of directories, and so is more portable.
In contrast to the native setup, the installer is free to relocate the tree
@@ -25,7 +25,7 @@
simdata = 'data/sim_files'
with open('README.md') as f:
LONG_DESCRIPTION = f.read()
-
+
# Create list of required modules for 'install_requires' parameter. Cannot create
# this list with pip.req.parse_requirements() because it requires the pwd module,
# which is Unix only.
@@ -42,11 +42,11 @@ def appdirGlob(globstr, subdir=''):
return glob.glob('/'.join([appdir, globstr]))
else:
return glob.glob('/'.join([appdir, subdir, globstr]))
-
+
class build_py(_build_py):
'''
Extends setuptools build of openvisualizer package data at installation time.
- Selects and copies the architecture-specific simulation module from an OS-based
+ Selects and copies the architecture-specific simulation module from an OS-based
subdirectory up to the parent 'sim_files' directory. Excludes the OS subdirectories
from installation.
'''
@@ -67,37 +67,37 @@ def build_package_data(self):
simPath = os.path.join(build_dir, 'data', 'sim_files')
target = os.path.join(simPath, 'oos_openwsn.{0}'.format(fileExt))
self.copy_file(srcfile, target)
-
+
if simPath:
shutil.rmtree(os.path.join(simPath, 'linux'))
shutil.rmtree(os.path.join(simPath, 'windows'))
setup(
name = 'openVisualizer',
- packages = ['openvisualizer',
- 'openvisualizer.BspEmulator', 'openvisualizer.eventBus',
- 'openvisualizer.lbrClient', 'openvisualizer.moteConnector',
- 'openvisualizer.moteProbe', 'openvisualizer.moteState',
- 'openvisualizer.openLbr', 'openvisualizer.openTun',
+ packages = ['openvisualizer',
+ 'openvisualizer.BspEmulator', 'openvisualizer.eventBus',
+ 'openvisualizer.lbrClient', 'openvisualizer.moteConnector',
+ 'openvisualizer.moteProbe', 'openvisualizer.moteState',
+ 'openvisualizer.openLbr', 'openvisualizer.openTun',
'openvisualizer.openType', 'openvisualizer.openUI',
'openvisualizer.RPL', 'openvisualizer.SimEngine', 'openvisualizer.remoteConnectorServer',
- 'openvisualizer.JRC'],
+ 'openvisualizer.JRC', 'openvisualizer.openBenchmarkAgent'],
scripts = appdirGlob('openVisualizer*.py'),
package_dir = {'': '.', 'openvisualizer': 'openvisualizer'},
# Copy simdata files by extension so don't copy .gitignore in that directory.
package_data = {'openvisualizer': [
'data/*.conf',
'data/requirements.txt',
- '/'.join([webstatic, 'css', '*']),
- '/'.join([webstatic, 'font-awesome', 'css', '*']),
- '/'.join([webstatic, 'font-awesome', 'fonts', '*']),
- '/'.join([webstatic, 'images', '*']),
- '/'.join([webstatic, 'js', '*.js']),
- '/'.join([webstatic, 'js', 'plugins', 'metisMenu', '*']),
- '/'.join([webtmpl, '*']),
- '/'.join([simdata, 'windows', '*.pyd']),
- '/'.join([simdata, 'linux', '*.so']),
- '/'.join([simdata, '*.h'])
+ '/'.join([webstatic, 'css', '*']),
+ '/'.join([webstatic, 'font-awesome', 'css', '*']),
+ '/'.join([webstatic, 'font-awesome', 'fonts', '*']),
+ '/'.join([webstatic, 'images', '*']),
+ '/'.join([webstatic, 'js', '*.js']),
+ '/'.join([webstatic, 'js', 'plugins', 'metisMenu', '*']),
+ '/'.join([webtmpl, '*']),
+ '/'.join([simdata, 'windows', '*.pyd']),
+ '/'.join([simdata, 'linux', '*.so']),
+ '/'.join([simdata, '*.h'])
]},
install_requires = deplist,
# Must extract zip to edit conf files.