diff --git a/tools/configure.ac b/tools/configure.ac index eb1a7fe476..a646c3955a 100755 --- a/tools/configure.ac +++ b/tools/configure.ac @@ -290,7 +290,6 @@ AC_OUTPUT( tinyos/python/misc/Makefile tinyos/python/packet/Makefile tinyos/python/tossim/Makefile - tinyos/python/utils/Makefile tinyos/safe/Makefile tinyos/safe/tos-decode-flid tinyos/safe/tos-ramsize diff --git a/tools/tinyos/python/Makefile.am b/tools/tinyos/python/Makefile.am index 767bf7a82e..19a1a4af0f 100644 --- a/tools/tinyos/python/Makefile.am +++ b/tools/tinyos/python/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = message misc packet tossim utils +SUBDIRS = message misc packet tossim tospy_PYTHON = tos.py __init__.py tospydir = $(pythondir)/tinyos diff --git a/tools/tinyos/python/__init__.py b/tools/tinyos/python/__init__.py index 1c3c4b0bd7..753e20fade 100644 --- a/tools/tinyos/python/__init__.py +++ b/tools/tinyos/python/__init__.py @@ -29,4 +29,4 @@ # Author: Geoffrey Mainland # -__all__ = ["message", "packet", "utils", "tossim", "misc"] +__all__ = ["message", "packet", "tossim", "misc"] diff --git a/tools/tinyos/python/message/MoteIF.py b/tools/tinyos/python/message/MoteIF.py index dc2453d7a8..5efd2cc0be 100644 --- a/tools/tinyos/python/message/MoteIF.py +++ b/tools/tinyos/python/message/MoteIF.py @@ -34,9 +34,8 @@ import struct import sys import traceback -from tinyos.utils.Watcher import Watcher -from tinyos.packet.Serial import Serial +from tinyos.packet.SerialH import Serial from tinyos.message.SerialPacket import SerialPacket import tinyos.packet.PacketDispatcher import tinyos.packet.PacketSource @@ -55,7 +54,6 @@ def __init__(self, *args): class MoteIF: def __init__(self): self.listeners = {} - self.watcher = Watcher.getInstance() def addListener(self, listener, msgClass): if listener not in self.listeners: @@ -93,8 +91,7 @@ def dispatchPacket(self, source, packet): print >>sys.stderr, x print >>sys.stderr, traceback.print_tb(sys.exc_info()[2]) - for l in self.listeners: - amTypes = self.listeners[l] + for l, amTypes in self.listeners.items(): if amType in amTypes: try: msgClass = amTypes[amType] diff --git a/tools/tinyos/python/packet/Makefile.am b/tools/tinyos/python/packet/Makefile.am index 262ba43723..f33fd1fe58 100644 --- a/tools/tinyos/python/packet/Makefile.am +++ b/tools/tinyos/python/packet/Makefile.am @@ -4,11 +4,11 @@ tospy_PYTHON = avrmote.py SerialIO.py SFProtocol.py __init__.py \ SerialSource.py ThreadTask.py tospydir = $(pythondir)/tinyos/packet -BUILT_SOURCES = Serial.py +BUILT_SOURCES = SerialH.py SERIAL_H = $(TINYOS_OS_DIR)/lib/serial/Serial.h TINYOS_OS_DIR ?= ../../../../tos TFLAGS = -I$(TINYOS_OS_DIR)/lib/serial -I$(TINYOS_OS_DIR)/types -Serial.py: +SerialH.py: nescc-ncg -o $@ $(TFLAGS) -python-classname=Serial python $(SERIAL_H) Serial.h diff --git a/tools/tinyos/python/packet/PacketSource.py b/tools/tinyos/python/packet/PacketSource.py index 209677a8fb..29102ae9cf 100644 --- a/tools/tinyos/python/packet/PacketSource.py +++ b/tools/tinyos/python/packet/PacketSource.py @@ -58,6 +58,7 @@ def __init__(self, dispatcher): self.semaphore = Semaphore(1) self.semaphore.acquire() + # this will be called in the new thread def __call__(self): try: self.open() @@ -112,6 +113,8 @@ def __call__(self): print "Unknown exception when dispatching packet" # break + self.cancel() + try: self.close() except: diff --git a/tools/tinyos/python/packet/SerialIO.py b/tools/tinyos/python/packet/SerialIO.py index a1ad4c84dc..71f6ee56d8 100644 --- a/tools/tinyos/python/packet/SerialIO.py +++ b/tools/tinyos/python/packet/SerialIO.py @@ -41,17 +41,21 @@ def __init__(self, device, baud): def open(self): self.serial = serial.Serial(port=self.device, - baudrate=self.baud) + baudrate=self.baud, timeout=1) def close(self): self.serial.close() def read(self, count): - while self.serial.inWaiting() < count: + data = "" + while count - len(data) > 0: if self.isDone(): raise IODone() - - return self.serial.read(count) + p = self.serial.read(count) + if len(p) == 0: + self.serial.inWaiting() # A workaround: raises IOException if USB serial device is disconnected + data += p + return data def write(self, data): return self.serial.write(data) diff --git a/tools/tinyos/python/packet/SerialProtocol.py b/tools/tinyos/python/packet/SerialProtocol.py index 1036c28e91..13f8478a89 100644 --- a/tools/tinyos/python/packet/SerialProtocol.py +++ b/tools/tinyos/python/packet/SerialProtocol.py @@ -35,7 +35,7 @@ from threading import Lock, Condition, Thread from IO import IODone -from Serial import Serial +from SerialH import Serial SYNC_BYTE = Serial.HDLC_FLAG_BYTE ESCAPE_BYTE = Serial.HDLC_CTLESC_BYTE @@ -88,15 +88,17 @@ def run(self): #OK, kind of ugly. finishing the SerialSource (ThreadTask) # leads (ultimately) to an IODone exception coming up # through here. At this point, the thread should complete. - except IODone: + except Exception, e: with self.prot.ackCV: self.prot.lastAck = None self.prot.ackCV.notify() with self.prot.dataCV: + self.prot.read_exception = e # storing exception to inform the other thread self.prot.lastData = None self.prot.dataCV.notify() break + class SerialProtocol: def __init__(self, ins, outs): self.ins = ins @@ -115,6 +117,7 @@ def __init__(self, ins, outs): self.ackCV = Condition(rxLock) self.lastData = None self.lastAck = None + self.read_exception = None #also a little ugly: can't start this thread until the # serial.Serial object has been opened. This should all be @@ -125,7 +128,10 @@ def open(self): def readPacket(self): with self.dataCV: + self.read_exception = None self.dataCV.wait() + if self.read_exception != None: + raise self.read_exception # an exception from the other thread return self.lastData def readFramedPacket(self): diff --git a/tools/tinyos/python/packet/SocketIO.py b/tools/tinyos/python/packet/SocketIO.py index f928021754..3ab7eb668c 100644 --- a/tools/tinyos/python/packet/SocketIO.py +++ b/tools/tinyos/python/packet/SocketIO.py @@ -57,8 +57,9 @@ def open(self): self.socket.settimeout(1) def close(self): - self.socket.close() - self.socket = None + if self.socket is not None: + self.socket.close() + self.socket = None def read(self, count): data = "" @@ -67,8 +68,11 @@ def read(self, count): raise IODone() try: - data += self.socket.recv(count - len(data)) - except: + p = self.socket.recv(count - len(data)) + if len(p) == 0: + raise IODone() # the remote side closed the connection + data += p + except socket.timeout: pass return data diff --git a/tools/tinyos/python/utils/Makefile.am b/tools/tinyos/python/utils/Makefile.am deleted file mode 100644 index 0716fa8d19..0000000000 --- a/tools/tinyos/python/utils/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -tospy_PYTHON = __init__.py Singleton.py Watcher.py - -tospydir = $(pythondir)/tinyos/utils - diff --git a/tools/tinyos/python/utils/Singleton.py b/tools/tinyos/python/utils/Singleton.py deleted file mode 100644 index 09d592441c..0000000000 --- a/tools/tinyos/python/utils/Singleton.py +++ /dev/null @@ -1,249 +0,0 @@ -# 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/tools/tinyos/python/utils/Watcher.py b/tools/tinyos/python/utils/Watcher.py deleted file mode 100644 index e96f466a36..0000000000 --- a/tools/tinyos/python/utils/Watcher.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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/tools/tinyos/python/utils/__init__.py b/tools/tinyos/python/utils/__init__.py deleted file mode 100644 index e1ac828fa2..0000000000 --- a/tools/tinyos/python/utils/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# 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"]