From: hiro Date: Fri, 14 Sep 2007 18:11:45 +0000 (+0000) Subject: Initial commit of Harvards Python SDK. Tested to work with current release including... X-Git-Tag: release_tinyos_2_1_0_0~734 X-Git-Url: https://oss.titaniummirror.com/gitweb/?p=tinyos-2.x.git;a=commitdiff_plain;h=7fe6f830a63c5a3817ea5615a99de1fbff9e7307 Initial commit of Harvards Python SDK. Tested to work with current release including TOSSIM. --- diff --git a/support/sdk/python/tinyos/__init__.py b/support/sdk/python/tinyos/__init__.py index f64fb74a..7b995667 100644 --- a/support/sdk/python/tinyos/__init__.py +++ b/support/sdk/python/tinyos/__init__.py @@ -28,4 +28,5 @@ # # Author: Geoffrey Mainland # -__all__ = ["message", "tossim"] + +__all__ = ["message", "packet", "utils", "tossim"] diff --git a/support/sdk/python/tinyos/message/Makefile b/support/sdk/python/tinyos/message/Makefile new file mode 100644 index 00000000..533ce2cb --- /dev/null +++ b/support/sdk/python/tinyos/message/Makefile @@ -0,0 +1,5 @@ +SERIAL_H = $(TOSDIR)/lib/serial/Serial.h + + +SerialPacket.py: + mig -o $@ -python-classname=SerialPacket python $(SERIAL_H) serial_packet -I$(TOSDIR)/types diff --git a/support/sdk/python/tinyos/message/Message.py b/support/sdk/python/tinyos/message/Message.py index 1d592fd2..d3befe03 100644 --- a/support/sdk/python/tinyos/message/Message.py +++ b/support/sdk/python/tinyos/message/Message.py @@ -46,11 +46,12 @@ class Message: 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 diff --git a/support/sdk/python/tinyos/message/MoteIF.py b/support/sdk/python/tinyos/message/MoteIF.py new file mode 100644 index 00000000..984b7798 --- /dev/null +++ b/support/sdk/python/tinyos/message/MoteIF.py @@ -0,0 +1,151 @@ +# +# 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 +# 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() diff --git a/support/sdk/python/tinyos/message/SerialPacket.py b/support/sdk/python/tinyos/message/SerialPacket.py new file mode 100644 index 00000000..8029a701 --- /dev/null +++ b/support/sdk/python/tinyos/message/SerialPacket.py @@ -0,0 +1,449 @@ +# +# 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 \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 + diff --git a/support/sdk/python/tinyos/message/__init__.py b/support/sdk/python/tinyos/message/__init__.py index a2d8cf6d..75859605 100644 --- a/support/sdk/python/tinyos/message/__init__.py +++ b/support/sdk/python/tinyos/message/__init__.py @@ -28,4 +28,4 @@ # # Author: Geoffrey Mainland # -__all__ = ["Message"] +__all__ = ["Message", "MoteIF", "SerialPacket"] diff --git a/support/sdk/python/tinyos/packet/IO.py b/support/sdk/python/tinyos/packet/IO.py new file mode 100644 index 00000000..b9053e8c --- /dev/null +++ b/support/sdk/python/tinyos/packet/IO.py @@ -0,0 +1,57 @@ +# +# 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 +# +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 diff --git a/support/sdk/python/tinyos/packet/Makefile b/support/sdk/python/tinyos/packet/Makefile new file mode 100644 index 00000000..77595676 --- /dev/null +++ b/support/sdk/python/tinyos/packet/Makefile @@ -0,0 +1,6 @@ +# 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 diff --git a/support/sdk/python/tinyos/packet/PacketDispatcher.py b/support/sdk/python/tinyos/packet/PacketDispatcher.py new file mode 100644 index 00000000..287f90c9 --- /dev/null +++ b/support/sdk/python/tinyos/packet/PacketDispatcher.py @@ -0,0 +1,59 @@ +# +# 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 +# +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(" +# +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 + diff --git a/support/sdk/python/tinyos/packet/Platform.py b/support/sdk/python/tinyos/packet/Platform.py new file mode 100644 index 00000000..66df0cc3 --- /dev/null +++ b/support/sdk/python/tinyos/packet/Platform.py @@ -0,0 +1,89 @@ +# +# 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 +# +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() diff --git a/support/sdk/python/tinyos/packet/SFProtocol.py b/support/sdk/python/tinyos/packet/SFProtocol.py new file mode 100644 index 00000000..831cea4a --- /dev/null +++ b/support/sdk/python/tinyos/packet/SFProtocol.py @@ -0,0 +1,77 @@ +# +# 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 +# +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() + diff --git a/support/sdk/python/tinyos/packet/SFSource.py b/support/sdk/python/tinyos/packet/SFSource.py new file mode 100644 index 00000000..90154015 --- /dev/null +++ b/support/sdk/python/tinyos/packet/SFSource.py @@ -0,0 +1,69 @@ +# +# 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 +# +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) diff --git a/support/sdk/python/tinyos/packet/Serial.py b/support/sdk/python/tinyos/packet/Serial.py new file mode 100644 index 00000000..27ae1f10 --- /dev/null +++ b/support/sdk/python/tinyos/packet/Serial.py @@ -0,0 +1,18 @@ + # + # 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 + diff --git a/support/sdk/python/tinyos/packet/SocketIO.py b/support/sdk/python/tinyos/packet/SocketIO.py new file mode 100644 index 00000000..f9280217 --- /dev/null +++ b/support/sdk/python/tinyos/packet/SocketIO.py @@ -0,0 +1,80 @@ +# +# 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 +# +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 diff --git a/support/sdk/python/tinyos/packet/ThreadTask.py b/support/sdk/python/tinyos/packet/ThreadTask.py new file mode 100644 index 00000000..5f1360ff --- /dev/null +++ b/support/sdk/python/tinyos/packet/ThreadTask.py @@ -0,0 +1,91 @@ +# +# 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 +# +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 diff --git a/support/sdk/python/tinyos/packet/__init__.py b/support/sdk/python/tinyos/packet/__init__.py new file mode 100644 index 00000000..9b5c173d --- /dev/null +++ b/support/sdk/python/tinyos/packet/__init__.py @@ -0,0 +1,33 @@ +# +# 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 +# +__all__ = ["PacketDispatcher", "PacketSource", "Packetizer", + "SFProtocol", "SFSource", "ThreadTask", + "avrmote", "micaz", "telos"] diff --git a/support/sdk/python/tinyos/utils/Singleton.py b/support/sdk/python/tinyos/utils/Singleton.py new file mode 100644 index 00000000..09d59244 --- /dev/null +++ b/support/sdk/python/tinyos/utils/Singleton.py @@ -0,0 +1,249 @@ +# Copyright (c) 2006-2007 Chad Metcalf +# +# 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 +# + +""" +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() diff --git a/support/sdk/python/tinyos/utils/Watcher.py b/support/sdk/python/tinyos/utils/Watcher.py new file mode 100644 index 00000000..e96f466a --- /dev/null +++ b/support/sdk/python/tinyos/utils/Watcher.py @@ -0,0 +1,72 @@ +# Copyright (c) 2006-2007 Chad Metcalf +# +# 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 +# + +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 diff --git a/support/sdk/python/tinyos/utils/__init__.py b/support/sdk/python/tinyos/utils/__init__.py new file mode 100644 index 00000000..e1ac828f --- /dev/null +++ b/support/sdk/python/tinyos/utils/__init__.py @@ -0,0 +1,24 @@ +# Copyright (c) 2006-2007 Chad Metcalf +# +# 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 +# + +__all__ = ["Singleton", "Watcher"]