#
# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
#
-__all__ = ["message", "tossim"]
+
+__all__ = ["message", "packet", "utils", "tossim"]
--- /dev/null
+SERIAL_H = $(TOSDIR)/lib/serial/Serial.h
+
+
+SerialPacket.py:
+ mig -o $@ -python-classname=SerialPacket python $(SERIAL_H) serial_packet -I$(TOSDIR)/types
if data == None or len(data) != data_length:
self.data = chr(0) * data_length
+
else:
self.data_length = len(data)
self.am_type = 0
-
+
def dataGet(self):
return self.data
--- /dev/null
+#
+# Copyright (c) 2005-2006
+# The President and Fellows of Harvard College.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
+# Tinyos-2: Stephen Dawson-Haggerty
+
+import os
+import re
+import struct
+import sys
+import traceback
+from tinyos.utils.Watcher import Watcher
+
+from tinyos.packet.Serial import Serial
+from tinyos.message.SerialPacket import SerialPacket
+import tinyos.packet.PacketDispatcher
+import tinyos.packet.PacketSource
+import tinyos.packet.SFSource
+try:
+ import tinyos.packet.SerialSource
+except:
+ tinyos.packet.SerialSource = None
+
+DEBUG = False
+
+class MoteIFException(Exception):
+ def __init__(self, *args):
+ self.args = args
+
+class MoteIF:
+ def __init__(self):
+ self.listeners = {}
+ self.watcher = Watcher.getInstance()
+
+ def addListener(self, listener, msgClass):
+ if listener not in self.listeners:
+ self.listeners[listener] = {}
+
+ amTypes = self.listeners[listener]
+ amTypes[msgClass.get_amType()] = msgClass
+
+ def removeListener(self, listener):
+ del self.listeners[listener]
+
+ def dispatchPacket(self, source, packet):
+ #try:
+ #print "Packet length: ", len(packet)
+ # print "Dispatching from MoteIF"
+ # for i in packet:
+ # print ord(i)," ",
+ # print
+ try:
+ # Message.py ignores base_offset, so we'll just chop off
+ # the first byte (the SERIAL_AMTYPE) here.
+ serial_pkt = SerialPacket(packet[1:],
+ data_length=len(packet)-1)
+ except:
+ traceback.print_exc()
+
+
+ try:
+ data_start = serial_pkt.offset_data(0) + 1
+ data_end = data_start + serial_pkt.get_header_length()
+ data = packet[data_start:data_end]
+ amType = serial_pkt.get_header_type()
+
+ except Exception, x:
+ print >>sys.stderr, x
+ print >>sys.stderr, traceback.print_tb(sys.exc_info()[2])
+
+ for l in self.listeners:
+ amTypes = self.listeners[l]
+ if amType in amTypes:
+ try:
+ msgClass = amTypes[amType]
+ msg = msgClass(data=data,
+ data_length = len(data),
+ addr=serial_pkt.get_header_src(),
+ gid=serial_pkt.get_header_group())
+ l.receive(source, msg)
+ except Exception, x:
+ print >>sys.stderr, x
+ print >>sys.stderr, traceback.print_tb(sys.exc_info()[2])
+
+ def sendMsg(self, dest, addr, amType, group, msg):
+ try:
+ payload = msg.dataGet()
+ msg = SerialPacket(None)
+ msg.set_header_dest(int(addr))
+ msg.set_header_group(int(group))
+ msg.set_header_type(int(amType))
+ msg.set_header_length(len(payload))
+
+ # from tinyos.packet.Serial
+ data = chr(Serial.TOS_SERIAL_ACTIVE_MESSAGE_ID)
+ data += msg.dataGet()[0:msg.offset_data(0)]
+ data += payload
+
+ dest.writePacket(data)
+ except Exception, x:
+ print >>sys.stderr, x
+ print >>sys.stderr, traceback.print_tb(sys.exc_info()[2])
+
+ def addSource(self, name=None):
+ if name == None:
+ name = os.environ.get("MOTECOM", "sf@localhost:9002")
+
+ m = re.match(r'([^@]*)@(.*)', name)
+ if m == None:
+ raise MoteIFException("base source '%s'" % (name))
+
+ (sourceType, args) = m.groups()
+
+ if sourceType == "sf":
+ source = tinyos.packet.SFSource.SFSource(self, args)
+ elif sourceType == "serial" and tinyos.packet.SerialSource != None:
+ source = tinyos.packet.SerialSource.SerialSource(self, args)
+ else:
+ raise MoteIFException("bad source")
+
+ source.start()
+
+ return source
+
+ def finishAll(self):
+ tinyos.packet.PacketSource.finishAll()
--- /dev/null
+#
+# This class is automatically generated by mig. DO NOT EDIT THIS FILE.
+# This class implements a Python interface to the 'SerialPacket'
+# message type.
+#
+
+import tinyos.message.Message
+
+# The default size of this message type in bytes.
+DEFAULT_MESSAGE_SIZE = 7
+
+# The Active Message type associated with this message.
+AM_TYPE = -1
+
+class SerialPacket(tinyos.message.Message.Message):
+ # Create a new SerialPacket of size 7.
+ def __init__(self, data="", addr=None, gid=None, base_offset=0, data_length=7):
+ tinyos.message.Message.Message.__init__(self, data, addr, gid, base_offset, data_length)
+ self.amTypeSet(AM_TYPE)
+
+ # Get AM_TYPE
+ def get_amType(cls):
+ return AM_TYPE
+
+ get_amType = classmethod(get_amType)
+
+ #
+ # Return a String representation of this message. Includes the
+ # message type name and the non-indexed field values.
+ #
+ def __str__(self):
+ s = "Message <SerialPacket> \n"
+ try:
+ s += " [header.dest=0x%x]\n" % (self.get_header_dest())
+ except:
+ pass
+ try:
+ s += " [header.src=0x%x]\n" % (self.get_header_src())
+ except:
+ pass
+ try:
+ s += " [header.length=0x%x]\n" % (self.get_header_length())
+ except:
+ pass
+ try:
+ s += " [header.group=0x%x]\n" % (self.get_header_group())
+ except:
+ pass
+ try:
+ s += " [header.type=0x%x]\n" % (self.get_header_type())
+ except:
+ pass
+ try:
+ pass
+ except:
+ pass
+ return s
+
+ # Message-type-specific access methods appear below.
+
+ #
+ # Accessor methods for field: header.dest
+ # Field type: int
+ # Offset (bits): 0
+ # Size (bits): 16
+ #
+
+ #
+ # Return whether the field 'header.dest' is signed (False).
+ #
+ def isSigned_header_dest(self):
+ return False
+
+ #
+ # Return whether the field 'header.dest' is an array (False).
+ #
+ def isArray_header_dest(self):
+ return False
+
+ #
+ # Return the offset (in bytes) of the field 'header.dest'
+ #
+ def offset_header_dest(self):
+ return (0 / 8)
+
+ #
+ # Return the offset (in bits) of the field 'header.dest'
+ #
+ def offsetBits_header_dest(self):
+ return 0
+
+ #
+ # Return the value (as a int) of the field 'header.dest'
+ #
+ def get_header_dest(self):
+ return self.getUIntElement(self.offsetBits_header_dest(), 16, 1)
+
+ #
+ # Set the value of the field 'header.dest'
+ #
+ def set_header_dest(self, value):
+ self.setUIntElement(self.offsetBits_header_dest(), 16, value, 1)
+
+ #
+ # Return the size, in bytes, of the field 'header.dest'
+ #
+ def size_header_dest(self):
+ return (16 / 8)
+
+ #
+ # Return the size, in bits, of the field 'header.dest'
+ #
+ def sizeBits_header_dest(self):
+ return 16
+
+ #
+ # Accessor methods for field: header.src
+ # Field type: int
+ # Offset (bits): 16
+ # Size (bits): 16
+ #
+
+ #
+ # Return whether the field 'header.src' is signed (False).
+ #
+ def isSigned_header_src(self):
+ return False
+
+ #
+ # Return whether the field 'header.src' is an array (False).
+ #
+ def isArray_header_src(self):
+ return False
+
+ #
+ # Return the offset (in bytes) of the field 'header.src'
+ #
+ def offset_header_src(self):
+ return (16 / 8)
+
+ #
+ # Return the offset (in bits) of the field 'header.src'
+ #
+ def offsetBits_header_src(self):
+ return 16
+
+ #
+ # Return the value (as a int) of the field 'header.src'
+ #
+ def get_header_src(self):
+ return self.getUIntElement(self.offsetBits_header_src(), 16, 1)
+
+ #
+ # Set the value of the field 'header.src'
+ #
+ def set_header_src(self, value):
+ self.setUIntElement(self.offsetBits_header_src(), 16, value, 1)
+
+ #
+ # Return the size, in bytes, of the field 'header.src'
+ #
+ def size_header_src(self):
+ return (16 / 8)
+
+ #
+ # Return the size, in bits, of the field 'header.src'
+ #
+ def sizeBits_header_src(self):
+ return 16
+
+ #
+ # Accessor methods for field: header.length
+ # Field type: short
+ # Offset (bits): 32
+ # Size (bits): 8
+ #
+
+ #
+ # Return whether the field 'header.length' is signed (False).
+ #
+ def isSigned_header_length(self):
+ return False
+
+ #
+ # Return whether the field 'header.length' is an array (False).
+ #
+ def isArray_header_length(self):
+ return False
+
+ #
+ # Return the offset (in bytes) of the field 'header.length'
+ #
+ def offset_header_length(self):
+ return (32 / 8)
+
+ #
+ # Return the offset (in bits) of the field 'header.length'
+ #
+ def offsetBits_header_length(self):
+ return 32
+
+ #
+ # Return the value (as a short) of the field 'header.length'
+ #
+ def get_header_length(self):
+ return self.getUIntElement(self.offsetBits_header_length(), 8, 1)
+
+ #
+ # Set the value of the field 'header.length'
+ #
+ def set_header_length(self, value):
+ self.setUIntElement(self.offsetBits_header_length(), 8, value, 1)
+
+ #
+ # Return the size, in bytes, of the field 'header.length'
+ #
+ def size_header_length(self):
+ return (8 / 8)
+
+ #
+ # Return the size, in bits, of the field 'header.length'
+ #
+ def sizeBits_header_length(self):
+ return 8
+
+ #
+ # Accessor methods for field: header.group
+ # Field type: short
+ # Offset (bits): 40
+ # Size (bits): 8
+ #
+
+ #
+ # Return whether the field 'header.group' is signed (False).
+ #
+ def isSigned_header_group(self):
+ return False
+
+ #
+ # Return whether the field 'header.group' is an array (False).
+ #
+ def isArray_header_group(self):
+ return False
+
+ #
+ # Return the offset (in bytes) of the field 'header.group'
+ #
+ def offset_header_group(self):
+ return (40 / 8)
+
+ #
+ # Return the offset (in bits) of the field 'header.group'
+ #
+ def offsetBits_header_group(self):
+ return 40
+
+ #
+ # Return the value (as a short) of the field 'header.group'
+ #
+ def get_header_group(self):
+ return self.getUIntElement(self.offsetBits_header_group(), 8, 1)
+
+ #
+ # Set the value of the field 'header.group'
+ #
+ def set_header_group(self, value):
+ self.setUIntElement(self.offsetBits_header_group(), 8, value, 1)
+
+ #
+ # Return the size, in bytes, of the field 'header.group'
+ #
+ def size_header_group(self):
+ return (8 / 8)
+
+ #
+ # Return the size, in bits, of the field 'header.group'
+ #
+ def sizeBits_header_group(self):
+ return 8
+
+ #
+ # Accessor methods for field: header.type
+ # Field type: short
+ # Offset (bits): 48
+ # Size (bits): 8
+ #
+
+ #
+ # Return whether the field 'header.type' is signed (False).
+ #
+ def isSigned_header_type(self):
+ return False
+
+ #
+ # Return whether the field 'header.type' is an array (False).
+ #
+ def isArray_header_type(self):
+ return False
+
+ #
+ # Return the offset (in bytes) of the field 'header.type'
+ #
+ def offset_header_type(self):
+ return (48 / 8)
+
+ #
+ # Return the offset (in bits) of the field 'header.type'
+ #
+ def offsetBits_header_type(self):
+ return 48
+
+ #
+ # Return the value (as a short) of the field 'header.type'
+ #
+ def get_header_type(self):
+ return self.getUIntElement(self.offsetBits_header_type(), 8, 1)
+
+ #
+ # Set the value of the field 'header.type'
+ #
+ def set_header_type(self, value):
+ self.setUIntElement(self.offsetBits_header_type(), 8, value, 1)
+
+ #
+ # Return the size, in bytes, of the field 'header.type'
+ #
+ def size_header_type(self):
+ return (8 / 8)
+
+ #
+ # Return the size, in bits, of the field 'header.type'
+ #
+ def sizeBits_header_type(self):
+ return 8
+
+ #
+ # Accessor methods for field: data
+ # Field type: short[]
+ # Offset (bits): 56
+ # Size of each element (bits): 8
+ #
+
+ #
+ # Return whether the field 'data' is signed (False).
+ #
+ def isSigned_data(self):
+ return False
+
+ #
+ # Return whether the field 'data' is an array (True).
+ #
+ def isArray_data(self):
+ return True
+
+ #
+ # Return the offset (in bytes) of the field 'data'
+ #
+ def offset_data(self, index1):
+ offset = 56
+ if index1 < 0:
+ raise IndexError
+ offset += 0 + index1 * 8
+ return (offset / 8)
+
+ #
+ # Return the offset (in bits) of the field 'data'
+ #
+ def offsetBits_data(self, index1):
+ offset = 56
+ if index1 < 0:
+ raise IndexError
+ offset += 0 + index1 * 8
+ return offset
+
+ #
+ # Return the entire array 'data' as a short[]
+ #
+ def get_data(self):
+ raise IndexError
+
+ #
+ # Set the contents of the array 'data' from the given short[]
+ #
+ def set_data(self, value):
+ for index0 in range(0, len(value)):
+ self.setElement_data(index0, value[index0])
+
+ #
+ # Return an element (as a short) of the array 'data'
+ #
+ def getElement_data(self, index1):
+ return self.getUIntElement(self.offsetBits_data(index1), 8, 1)
+
+ #
+ # Set an element of the array 'data'
+ #
+ def setElement_data(self, index1, value):
+ self.setUIntElement(self.offsetBits_data(index1), 8, value, 1)
+
+ #
+ # Return the size, in bytes, of each element of the array 'data'
+ #
+ def elementSize_data(self):
+ return (8 / 8)
+
+ #
+ # Return the size, in bits, of each element of the array 'data'
+ #
+ def elementSizeBits_data(self):
+ return 8
+
+ #
+ # Return the number of dimensions in the array 'data'
+ #
+ def numDimensions_data(self):
+ return 1
+
+ #
+ # Return the number of elements in the array 'data'
+ # for the given dimension.
+ #
+ def numElements_data(self, dimension):
+ array_dims = [ 0, ]
+ if dimension < 0 or dimension >= 1:
+ raise IndexException
+ if array_dims[dimension] == 0:
+ raise IndexError
+ return array_dims[dimension]
+
+ #
+ # Fill in the array 'data' with a String
+ #
+ def setString_data(self, s):
+ l = len(s)
+ for i in range(0, l):
+ self.setElement_data(i, ord(s[i]));
+ self.setElement_data(l, 0) #null terminate
+
+ #
+ # Read the array 'data' as a String
+ #
+ def getString_data(self):
+ carr = "";
+ for i in range(0, 4000):
+ if self.getElement_data(i) == chr(0):
+ break
+ carr += self.getElement_data(i)
+ return carr
+
#
# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
#
-__all__ = ["Message"]
+__all__ = ["Message", "MoteIF", "SerialPacket"]
--- /dev/null
+#
+# Copyright (c) 2005
+# The President and Fellows of Harvard College.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
+#
+class IODone(Exception):
+ pass
+
+class IO:
+ def __init__(self):
+ self.done = False
+
+ def isDone(self):
+ return self.done
+
+ def cancel(self):
+ self.done = True
+
+ def open(self):
+ pass
+
+ def close(self):
+ pass
+
+ def read(self, count):
+ pass
+
+ def write(self, data):
+ pass
+
+ def flush(self):
+ pass
--- /dev/null
+# Makefile for tools/java/net/tinyos/packet
+
+SERIAL_H = $(TOSDIR)/lib/serial/Serial.h
+
+Serial.py:
+ ncg -o $@ -python-classname=Serial python $(SERIAL_H) Serial.h
--- /dev/null
+#
+# Copyright (c) 2005
+# The President and Fellows of Harvard College.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
+#
+import struct
+
+class PacketDispatcher:
+ def __init__(self):
+ self.listeners = {}
+
+ def addListener(self, listener, msgClass):
+ if listener not in self.listeners:
+ self.listeners[listener] = {}
+
+ amTypes = self.listeners[listener]
+ amTypes[msgClass.get_amType()] = msgClass
+
+ def removeListener(self, listener):
+ del self.listeners[listener]
+
+ def dispatchPacket(self, source, packet):
+ (addr, amType, group, length) = struct.unpack("<HBBB", packet[0:5])
+ msgData = packet[5:]
+
+ #print (addr, amType, group, length)
+
+ for l in self.listeners:
+ amTypes = self.listeners[l]
+ if amType in amTypes:
+ msgClass = amTypes[amType]
+ msg = msgClass(msgData)
+
+ l.receive(source, msg)
--- /dev/null
+#
+# Copyright (c) 2005-2006
+# The President and Fellows of Harvard College.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
+#
+import signal
+import sys
+import traceback
+
+from IO import *
+from ThreadTask import *
+
+DEBUG = False
+
+runner = ThreadTaskRunner()
+
+def finishAll():
+ global runner
+
+ runner.cancelAll()
+ runner.finish()
+
+class PacketSourceException(Exception):
+ def __init__(self, *args):
+ self.args = args
+
+class PacketSource(ThreadTask):
+ def __init__(self, dispatcher):
+ global runner
+ ThreadTask.__init__(self, runner)
+ self.dispatcher = dispatcher
+
+ def __call__(self):
+ try:
+ self.open()
+ except Exception, x:
+ if DEBUG:
+ print "Exception while opening packet source:"
+ print x
+ print traceback.print_tb(sys.exc_info()[2])
+ self.done = True
+ except:
+ if DEBUG:
+ print "Unknown exception while opening packet source"
+ self.done = True
+
+ while not self.isDone():
+ try:
+ packet = self.readPacket()
+ except IODone:
+ if DEBUG:
+ print "IO finished"
+ break
+ except Exception, x:
+ if DEBUG:
+ print "IO exception:"
+ print x
+ print traceback.print_tb(sys.exc_info()[2])
+ break
+ except:
+ if DEBUG:
+ print "Unknown IO exception"
+ break
+
+ if packet:
+ try:
+# print "About to run packet dispatcher!"
+# for i in packet:
+# print ord(i)," ",
+# print
+
+ self.dispatcher.dispatchPacket(self, packet)
+ except Exception, x:
+ if DEBUG:
+ print "Exception when dispatching packet:"
+ print x
+ print traceback.print_tb(sys.exc_info()[2])
+# break
+ except:
+ if DEBUG:
+ print "Unknown exception when dispatching packet"
+# break
+
+ try:
+ self.close()
+ except:
+ pass
+
+ self.finish()
+
+ def start(self):
+ global runner
+
+ runner.start(self)
+
+ def open(self):
+ pass
+
+ def close(self):
+ pass
+
+ def readPacket(self):
+ return None
+
+ def writePacket(self, packet):
+ pass
+
--- /dev/null
+#
+# Copyright (c) 2006
+# The President and Fellows of Harvard College.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
+#
+import re
+import socket
+import sys
+import traceback
+
+DEBUG = False
+
+PLATFORMS = {"mica": ("avrmote", 1, 19200),
+ "mica2dot": ("avrmote", 1, 19200),
+ "mica2": ("avrmote", 1, 57600),
+ "telos": ("telos", 2, 57600),
+ "tmote": ("telos", 2, 57600),
+ "micaz": ("avrmote", 3, 57600),
+ "eyes": ("eyes", 4, 19200)}
+
+ID_AVRMOTE = 1
+ID_TELOS = 2
+ID_MICAZ = 3
+ID_EYES = 4
+
+DEFAULT_BAUD = 19200
+
+class UnknownPlatform(Exception):
+ pass
+
+def baud_from_name(name):
+ try:
+ return PLATFORMS[name][2]
+ except:
+ raise UnknownPlatform()
+
+def default_factory():
+ return factory_from_platform("avrmote")
+
+def factory_from_name(name):
+ try:
+ return factory_from_platform(PLATFORMS[name][0])
+ except:
+ raise UnknownPlatform()
+
+def factory_from_id(i):
+ if i == ID_AVRMOTE:
+ return factory_from_platform("avrmote")
+ elif i == ID_TELOS:
+ return factory_from_platform("telos")
+ elif i == ID_MICAZ:
+ return factory_from_platform("avrmote")
+ else:
+ raise UnknownPlatform()
+
+def factory_from_platform(platform):
+ try:
+ mod = __import__("tinyos.packet.%s" % platform)
+ return mod.packet.__dict__[platform].TOS_Msg
+ except Exception, x:
+ if DEBUG:
+ print >>sys.stderr, x
+ print >>sys.stderr, traceback.print_tb(sys.exc_info()[2])
+ raise UnknownPlatform()
--- /dev/null
+#
+# Copyright (c) 2005-2006
+# The President and Fellows of Harvard College.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
+#
+VERSION = "U"
+SUBVERSION = " "
+
+PLATFORM_UNKNOWN = 0
+
+class SFProtocolException(Exception):
+ def __init__(self, *args):
+ self.args = args
+
+class SFProtocol:
+ def __init__(self, ins, outs):
+ self.ins = ins
+ self.outs = outs
+ self.platform = None
+
+ def open(self):
+ self.outs.write(VERSION + SUBVERSION)
+ partner = self.ins.read(2)
+ if partner[0] != VERSION:
+ print "SFProtocol : version error"
+ raise SFProtocolException("protocol version error")
+
+ # Actual version is min received vs our version
+ # ourversion = partner[1] & 0xff
+
+ if self.platform == None:
+ self.platform = PLATFORM_UNKNOWN
+
+ # In tinyox-1.x, we then exchanged platform information
+
+ # the tinyos-2.x serial forwarder doesn't do that, so the
+ # connection is all set up at this point.
+
+
+ def readPacket(self):
+ size = self.ins.read(1)
+ packet = self.ins.read(ord(size))
+ return packet
+
+ def writePacket(self, packet):
+ if len(packet) > 255:
+ raise SFProtocolException("packet too long")
+
+ self.outs.write(chr(len(packet)))
+ self.outs.write(packet)
+ self.outs.flush()
+
--- /dev/null
+#
+# Copyright (c) 2005-2006
+# The President and Fellows of Harvard College.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
+#
+import re
+import socket
+
+from PacketSource import *
+from Platform import *
+from SFProtocol import *
+from SocketIO import *
+
+class SFSource(PacketSource):
+ def __init__(self, dispatcher, args):
+ PacketSource.__init__(self, dispatcher)
+
+ m = re.match(r'(.*):(.*)', args)
+ if m == None:
+ raise PacketSourceException("bad arguments")
+
+ (host, port) = m.groups()
+ port = int(port)
+
+ self.io = SocketIO(host, port)
+ self.prot = SFProtocol(self.io, self.io)
+
+ def cancel(self):
+ self.done = True
+ self.io.cancel()
+
+ def open(self):
+ self.io.open()
+ self.prot.open()
+ PacketSource.open(self)
+
+ def close(self):
+ self.io.close()
+
+ def readPacket(self):
+ return self.prot.readPacket()
+
+ def writePacket(self, packet):
+ self.prot.writePacket(packet)
--- /dev/null
+ #
+ # This class is automatically generated by ncg. DO NOT EDIT THIS FILE.
+ # This class includes values of some nesC constants from
+ # /opt/tinyos-2.x/tos/lib/serial/Serial.h.
+ #/
+
+class Serial:
+ HDLC_CTLESC_BYTE = 125
+ SERIAL_PROTO_ACK = 67
+ TOS_SERIAL_802_15_4_ID = 2
+ SERIAL_PROTO_PACKET_UNKNOWN = 255
+ SERIAL_PROTO_PACKET_NOACK = 69
+ TOS_SERIAL_CC1000_ID = 1
+ HDLC_FLAG_BYTE = 126
+ TOS_SERIAL_ACTIVE_MESSAGE_ID = 0
+ SERIAL_PROTO_PACKET_ACK = 68
+ TOS_SERIAL_UNKNOWN_ID = 255
+
--- /dev/null
+#
+# Copyright (c) 2005
+# The President and Fellows of Harvard College.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
+#
+import socket
+
+from IO import *
+
+class SocketIO(IO):
+ def __init__(self, host, port):
+ IO.__init__(self)
+
+ self.done = False
+
+ self.host = host
+ self.port = port
+
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.socket.setsockopt(socket.IPPROTO_TCP,
+ socket.TCP_NODELAY,
+ 1)
+ self.socket.settimeout(1)
+ self.socket.bind(("", 0))
+
+ def cancel(self):
+ self.done = True
+
+ def open(self):
+ print "SocketIO: Connecting socket to "+str(self.host)+":"+str(self.port)
+ self.socket.connect((self.host, self.port))
+ self.socket.settimeout(1)
+
+ def close(self):
+ self.socket.close()
+ self.socket = None
+
+ def read(self, count):
+ data = ""
+ while count - len(data) > 0:
+ if self.isDone():
+ raise IODone()
+
+ try:
+ data += self.socket.recv(count - len(data))
+ except:
+ pass
+
+ return data
+
+ def write(self, data):
+ return self.socket.send(data)
+
+ def flush(self):
+ pass
--- /dev/null
+#
+# Copyright (c) 2005
+# The President and Fellows of Harvard College.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
+#
+import threading
+import time
+
+class ThreadTask:
+ def __init__(self, runner):
+ self.done = False
+ self.runner = runner
+
+ runner.add(self)
+
+ def isDone(self):
+ return self.done
+
+ def cancel(self):
+ self.done = True
+
+ def finish(self):
+ self.runner.remove(self)
+
+class ThreadTaskRunner:
+ def __init__(self):
+ self.taskList = []
+ self.taskListLock = threading.Lock()
+
+ def add(self, task):
+ self.taskListLock.acquire()
+ self.taskList = [task] + self.taskList
+ self.taskListLock.release()
+
+ def remove(self, task):
+ self.taskListLock.acquire()
+ self.taskList.remove(task)
+ self.taskListLock.release()
+
+ def start(self, task):
+ thread = threading.Thread(None, task)
+ thread.start()
+
+ def cancelAll(self):
+ self.taskListLock.acquire()
+
+ for t in self.taskList:
+ try:
+ t.cancel()
+ except:
+ pass
+
+ self.taskListLock.release()
+
+ def finish(self):
+ try:
+ self.taskListLock.acquire()
+
+ while len(self.taskList) != 0:
+ self.taskListLock.release()
+ time.sleep(0.2)
+ self.taskListLock.acquire()
+
+ self.taskListLock.release()
+ except:
+ pass
--- /dev/null
+#
+# Copyright (c) 2005
+# The President and Fellows of Harvard College.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Author: Geoffrey Mainland <mainland@eecs.harvard.edu>
+#
+__all__ = ["PacketDispatcher", "PacketSource", "Packetizer",
+ "SFProtocol", "SFSource", "ThreadTask",
+ "avrmote", "micaz", "telos"]
--- /dev/null
+# Copyright (c) 2006-2007 Chad Metcalf <chad@5secondfuse.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Author: Chad Metcalf <chad@5secondfuse.com>
+#
+
+"""
+A Python Singleton mixin class that makes use of some of the ideas
+found at http://c2.com/cgi/wiki?PythonSingleton. Just inherit
+from it and you have a singleton. No code is required in
+subclasses to create singleton behavior -- inheritance from
+Singleton is all that is needed.
+
+Assume S is a class that inherits from Singleton. Useful behaviors
+are:
+
+1) Getting the singleton:
+
+ S.getInstance()
+
+returns the instance of S. If none exists, it is created.
+
+2) The usual idiom to construct an instance by calling the class, i.e.
+
+ S()
+
+is disabled for the sake of clarity. If it were allowed, a programmer
+who didn't happen notice the inheritance from Singleton might think he
+was creating a new instance. So it is felt that it is better to
+make that clearer by requiring the call of a class method that is defined in
+Singleton. An attempt to instantiate via S() will restult in an SingletonException
+being raised.
+
+3) If S.__init__(.) requires parameters, include them in the
+first call to S.getInstance(.). If subsequent calls have parameters,
+a SingletonException is raised.
+
+4) As an implementation detail, classes that inherit
+from Singleton may not have their own __new__
+methods. To make sure this requirement is followed,
+an exception is raised if a Singleton subclass includ
+es __new__. This happens at subclass instantiation
+time (by means of the MetaSingleton metaclass.
+
+By Gary Robinson, grobinson@transpose.com. No rights reserved --
+placed in the public domain -- which is only reasonable considering
+how much it owes to other people's version which are in the
+public domain. The idea of using a metaclass came from
+a comment on Gary's blog (see
+http://www.garyrobinson.net/2004/03/python_singleto.html#comments).
+Other improvements came from comments and email from other
+people who saw it online. (See the blog post and comments
+for further credits.)
+
+Not guaranteed to be fit for any particular purpose. Use at your
+own risk.
+"""
+
+class SingletonException(Exception):
+ def __init__(self, *args):
+ Exception.__init__(self)
+ self.args = args
+
+class MetaSingleton(type):
+ def __new__(metaclass, strName, tupBases, dict):
+ if dict.has_key('__new__'):
+ raise SingletonException, 'Can not override __new__ in a Singleton'
+ return super(MetaSingleton,metaclass).__new__(metaclass, strName, tupBases, dict)
+
+ def __call__(cls, *lstArgs, **dictArgs):
+ raise SingletonException, 'Singletons may only be instantiated through getInstance()'
+
+class Singleton(object):
+ __metaclass__ = MetaSingleton
+
+ def getInstance(cls, *lstArgs):
+ """
+ Call this to instantiate an instance or retrieve the existing instance.
+ If the singleton requires args to be instantiated, include them the first
+ time you call getInstance.
+ """
+ if cls._isInstantiated():
+ if len(lstArgs) != 0:
+ raise SingletonException, 'If no supplied args, singleton must already be instantiated, or __init__ must require no args'
+ else:
+ if cls._getConstructionArgCountNotCountingSelf() > 0 and len(lstArgs) <= 0:
+ raise SingletonException, 'If the singleton requires __init__ args, supply them on first instantiation'
+ instance = cls.__new__(cls)
+ instance.__init__(*lstArgs)
+ cls.cInstance = instance
+ return cls.cInstance
+ getInstance = classmethod(getInstance)
+
+ def _isInstantiated(cls):
+ return hasattr(cls, 'cInstance')
+ _isInstantiated = classmethod(_isInstantiated)
+
+ def _getConstructionArgCountNotCountingSelf(cls):
+ return cls.__init__.im_func.func_code.co_argcount - 1
+ _getConstructionArgCountNotCountingSelf = classmethod(_getConstructionArgCountNotCountingSelf)
+
+ def _forgetClassInstanceReferenceForTesting(cls):
+ """
+ This is designed for convenience in testing -- sometimes you
+ want to get rid of a singleton during test code to see what
+ happens when you call getInstance() under a new situation.
+
+ To really delete the object, all external references to it
+ also need to be deleted.
+ """
+ try:
+ delattr(cls,'cInstance')
+ except AttributeError:
+ # run up the chain of base classes until we find the one that has the instance
+ # and then delete it there
+ for baseClass in cls.__bases__:
+ if issubclass(baseClass, Singleton):
+ baseClass._forgetClassInstanceReferenceForTesting()
+ _forgetClassInstanceReferenceForTesting = classmethod(_forgetClassInstanceReferenceForTesting)
+
+
+
+if __name__ == '__main__':
+ import unittest
+
+ class PublicInterfaceTest(unittest.TestCase):
+ def testReturnsSameObject(self):
+ """
+ Demonstrates normal use -- just call getInstance and it returns a singleton instance
+ """
+
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+
+ a1 = A.getInstance()
+ a2 = A.getInstance()
+ self.assertEquals(id(a1), id(a2))
+
+ def testInstantiateWithMultiArgConstructor(self):
+ """
+ If the singleton needs args to construct, include them in the first
+ call to get instances.
+ """
+
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+
+ b1 = B.getInstance('arg1 value', 'arg2 value')
+ b2 = B.getInstance()
+ self.assertEquals(b1.arg1, 'arg1 value')
+ self.assertEquals(b1.arg2, 'arg2 value')
+ self.assertEquals(id(b1), id(b2))
+
+
+ def testTryToInstantiateWithoutNeededArgs(self):
+
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+
+ self.assertRaises(SingletonException, B.getInstance)
+
+ def testTryToInstantiateWithoutGetInstance(self):
+ """
+ Demonstrates that singletons can ONLY be instantiated through
+ getInstance, as long as they call Singleton.__init__ during construction.
+
+ If this check is not required, you don't need to call Singleton.__init__().
+ """
+
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+
+ self.assertRaises(SingletonException, A)
+
+ def testDontAllowNew(self):
+
+ def instantiatedAnIllegalClass():
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+
+ def __new__(metaclass, strName, tupBases, dict):
+ return super(MetaSingleton,metaclass).__new__(metaclass, strName, tupBases, dict)
+
+ self.assertRaises(SingletonException, instantiatedAnIllegalClass)
+
+
+ def testDontAllowArgsAfterConstruction(self):
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+
+ b1 = B.getInstance('arg1 value', 'arg2 value')
+ self.assertRaises(SingletonException, B, 'arg1 value', 'arg2 value')
+
+ def test_forgetClassInstanceReferenceForTesting(self):
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+ class B(A):
+ def __init__(self):
+ super(B, self).__init__()
+
+ # check that changing the class after forgetting the instance produces
+ # an instance of the new class
+ a = A.getInstance()
+ assert a.__class__.__name__ == 'A'
+ A._forgetClassInstanceReferenceForTesting()
+ b = B.getInstance()
+ assert b.__class__.__name__ == 'B'
+
+ # check that invoking the 'forget' on a subclass still deletes the instance
+ B._forgetClassInstanceReferenceForTesting()
+ a = A.getInstance()
+ B._forgetClassInstanceReferenceForTesting()
+ b = B.getInstance()
+ assert b.__class__.__name__ == 'B'
+
+ unittest.main()
--- /dev/null
+# Copyright (c) 2006-2007 Chad Metcalf <chad@5secondfuse.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Author: Chad Metcalf <chad@5secondfuse.com>
+#
+
+import os
+import sys
+import signal
+
+from tinyos.utils.Singleton import Singleton
+
+class Watcher(Singleton):
+ """ As seen in:
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496735
+
+ This class solves two problems with multithreaded
+ programs in Python, (1) a signal might be delivered
+ to any thread (which is just a malfeature) and (2) if
+ the thread that gets the signal is waiting, the signal
+ is ignored (which is a bug).
+
+ The watcher is a concurrent process (not thread) that
+ waits for a signal and the process that contains the
+ threads. See Appendix A of The Little Book of Semaphores.
+ http://greenteapress.com/semaphores/
+ """
+
+ def __init__(self):
+ """ Creates a child thread, which returns. The parent
+ thread waits for a KeyboardInterrupt and then kills
+ the child thread.
+ """
+ Singleton.__init__(self)
+
+ self.child = os.fork()
+ if self.child != 0:
+ self.watch()
+
+ def watch(self):
+ try:
+ os.wait()
+ except KeyboardInterrupt:
+ # I put the capital B in KeyBoardInterrupt so I can
+ # tell when the Watcher gets the SIGINT
+ print 'KeyBoardInterrupt'
+ self.kill()
+ sys.exit()
+
+ def kill(self):
+ try:
+ os.kill(self.child, signal.SIGKILL)
+ except OSError, x:
+ print "os.kill failed"
+ print x
\ No newline at end of file
--- /dev/null
+# Copyright (c) 2006-2007 Chad Metcalf <chad@5secondfuse.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Author: Chad Metcalf <chad@5secondfuse.com>
+#
+
+__all__ = ["Singleton", "Watcher"]