From 34d26870bda4aaaf1306b27f535b39ff3fc011f2 Mon Sep 17 00:00:00 2001 From: razvanm Date: Mon, 15 Dec 2008 00:50:37 +0000 Subject: [PATCH] Improvements to tos.py. This latest version includes: * support for MIB600 * a generic dump program (tos-dump) that has support for decoding the printf packets * a few example programs for some of the applications (Oscilloscope, MultihopOscilloscope, TestFtsp, TestLocalTime). --- apps/MultihopOscilloscope/oscilloscope.py | 30 + apps/Oscilloscope/oscilloscope.py | 29 + apps/tests/TestFtsp/FtspDataLogger.py | 33 + apps/tests/TestLocalTime/README.txt | 3 + apps/tests/TestLocalTime/dump.py | 22 + apps/tests/TestSerial/README.txt | 3 + apps/tests/deluge/Blink/burn | 57 +- apps/tests/deluge/Blink/burn-net | 36 +- support/sdk/python/tos.py | 946 ++++++++-------------- tools/tinyos/misc/Makefile.am | 3 +- tools/tinyos/misc/tos-deluge | 54 +- tools/tinyos/misc/tos-dump.py | 17 + 12 files changed, 530 insertions(+), 703 deletions(-) create mode 100644 apps/MultihopOscilloscope/oscilloscope.py create mode 100644 apps/Oscilloscope/oscilloscope.py create mode 100644 apps/tests/TestFtsp/FtspDataLogger.py create mode 100644 apps/tests/TestLocalTime/dump.py create mode 100644 tools/tinyos/misc/tos-dump.py diff --git a/apps/MultihopOscilloscope/oscilloscope.py b/apps/MultihopOscilloscope/oscilloscope.py new file mode 100644 index 00000000..de29c89d --- /dev/null +++ b/apps/MultihopOscilloscope/oscilloscope.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +import sys +import tos + +AM_OSCILLOSCOPE = 0x93 + +class OscilloscopeMsg(tos.Packet): + def __init__(self, packet = None): + tos.Packet.__init__(self, + [('version', 'int', 2), + ('interval', 'int', 2), + ('id', 'int', 2), + ('count', 'int', 2), + ('readings', 'blob', None)], + packet) + +if '-h' in sys.argv: + print "Usage:", sys.argv[0], "serial@/dev/ttyUSB0:57600" + sys.exit() + +am = tos.AM() + +while True: + p = am.read() + if p and p.type == AM_OSCILLOSCOPE: + msg = OscilloscopeMsg(p.data) + print msg.id, msg.count, [i<<8 | j for (i,j) in zip(msg.readings[::2], msg.readings[1::2])] + #print msg + diff --git a/apps/Oscilloscope/oscilloscope.py b/apps/Oscilloscope/oscilloscope.py new file mode 100644 index 00000000..3b881add --- /dev/null +++ b/apps/Oscilloscope/oscilloscope.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +import sys +import tos + +AM_OSCILLOSCOPE = 0x93 + +class OscilloscopeMsg(tos.Packet): + def __init__(self, packet = None): + tos.Packet.__init__(self, + [('version', 'int', 2), + ('interval', 'int', 2), + ('id', 'int', 2), + ('count', 'int', 2), + ('readings', 'blob', None)], + packet) +if '-h' in sys.argv: + print "Usage:", sys.argv[0], "serial@/dev/ttyUSB0:57600" + sys.exit() + +am = tos.AM() + +while True: + p = am.read() + if p and p.type == AM_OSCILLOSCOPE: + msg = OscilloscopeMsg(p.data) + print msg.id, msg.count, [i<<8 | j for (i,j) in zip(msg.readings[::2], msg.readings[1::2])] + #print msg + diff --git a/apps/tests/TestFtsp/FtspDataLogger.py b/apps/tests/TestFtsp/FtspDataLogger.py new file mode 100644 index 00000000..0c5687b4 --- /dev/null +++ b/apps/tests/TestFtsp/FtspDataLogger.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +import sys, time +import tos + +AM_TEST_FTSP_MSG = 137 + +class FtspMsg(tos.Packet): + def __init__(self, packet = None): + tos.Packet.__init__(self, + [('src_addr', 'int', 2), + ('counter', 'int', 2), + ('local_rx_timestamp', 'int', 4), + ('global_rx_timestamp', 'int', 4), + ('skew_times_1000000', 'int', 4), + ('is_synced', 'int', 1), + ('ftsp_root_addr', 'int', 2), + ('ftsp_seq', 'int', 1), + ('ftsp_table_entries', 'int', 2)], + packet) + +if '-h' in sys.argv: + print "Usage:", sys.argv[0], "serial@/dev/ttyUSB0:57600" + sys.exit() + +am = tos.AM() + +while True: + p = am.read() + if p and p.type == AM_TEST_FTSP_MSG: + msg = FtspMsg(p.data) + print int(time.time()), msg.src_addr, msg.counter, msg.global_rx_timestamp, msg.is_synced + #print msg diff --git a/apps/tests/TestLocalTime/README.txt b/apps/tests/TestLocalTime/README.txt index 5fac5fae..825967f4 100644 --- a/apps/tests/TestLocalTime/README.txt +++ b/apps/tests/TestLocalTime/README.txt @@ -6,6 +6,9 @@ Description: TestLocalTime is a simple application that tests the LocalTimeMilliC component by sending the current time over the serial port once per second. +dump.py is a Python script that can be used to read and format the values from +the serial port. + Tools: None. diff --git a/apps/tests/TestLocalTime/dump.py b/apps/tests/TestLocalTime/dump.py new file mode 100644 index 00000000..e8dea05f --- /dev/null +++ b/apps/tests/TestLocalTime/dump.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +import sys +import tos + +class Localtime(tos.Packet): + def __init__(self, packet = None): + tos.Packet.__init__(self, + [('time', 'int', 4)], + packet) + +if '-h' in sys.argv: + print "Usage:", sys.argv[0], "serial@/dev/ttyUSB0:57600" + sys.exit() + +am = tos.AM() + +while True: + p = am.read() + if p: + msg = Localtime(p.data) + print msg diff --git a/apps/tests/TestSerial/README.txt b/apps/tests/TestSerial/README.txt index ca16d9b1..1a8e56d5 100644 --- a/apps/tests/TestSerial/README.txt +++ b/apps/tests/TestSerial/README.txt @@ -19,6 +19,9 @@ Java Application Usage: If not specified, the defaults to sf@localhost:9002 or to your MOTECOM environment variable (if defined). +Python Usage: + tos-dump /dev/ttyUSB0 57600 + Tools: Known bugs/limitations: diff --git a/apps/tests/deluge/Blink/burn b/apps/tests/deluge/Blink/burn index e43e7b0c..5622fdfc 100755 --- a/apps/tests/deluge/Blink/burn +++ b/apps/tests/deluge/Blink/burn @@ -1,41 +1,35 @@ #!/bin/bash -python -c ' -import sys -try: - import serial -except ImportError, e: - sys.exit(1)' +TOS_DELUGE=`type -p tos-deluge` +if [[ ! -x ${TOS_DELUGE} ]] ; then + TOS_DELUGE=../../../../tools/tinyos/misc/tos-deluge +fi + +$TOS_DELUGE > /dev/null if [[ $? != 0 ]] then - echo "Please install PySerial first." + echo "Unable to locate tos-deluge." exit 2 fi -TOS_DELUGE=`type -p tos-deluge` -if [[ ! -x ${TOS_DELUGE} ]] ; then - TOS_DELUGE=../../../../tools/tinyos/misc/tos-deluge -fi - if [[ $# -ne 2 && $# -ne 3 ]]; then - echo "Usage: $0 [] " - echo " /dev/ttyUSB0" - echo " /dev/ttyUSB1" - echo " micaz, telosb, iris or epic" + echo "Usage: $0 " + echo " bsl,PORT | mib510,PORT | eprb,PORT" + echo " serial@PORT:SPEED | network@HOST:PORT" + echo " micaz | telosb | iris | epic" exit 2 fi PPORT=$1 -CPORT=$1 -PLATFORM=$2 - -if [ $# -eq 3 ]; then - CPORT=$2 - PLATFORM=$3 -fi +CPORT=$2 +PLATFORM=$3 -if [ ${PLATFORM} != 'micaz' -a ${PLATFORM} != 'telosb' -a ${PLATFORM} != 'iris' -a ${PLATFORM} != 'epic' ]; then +if [ ${PLATFORM} != 'micaz' -a \ + ${PLATFORM} != 'telosb' -a \ + ${PLATFORM} != 'iris' -a \ + ${PLATFORM} != 'epic' ] +then echo "\"${PLATFORM}\" is not a supported platform" exit 2 fi @@ -49,16 +43,7 @@ fi make clean echo ============================ Compile and load Blink ============================ -if [ $PLATFORM == 'micaz' ] -then - CFLAGS=-DDELUGE_BASESTATION make ${PLATFORM} install mib510,${PPORT} -elif [ $PLATFORM == 'telosb' -o $PLATFORM == 'epic' ] -then - CFLAGS=-DDELUGE_BASESTATION make ${PLATFORM} install bsl,${PPORT} -elif [ $PLATFORM == 'iris' ] -then - CFLAGS=-DDELUGE_BASESTATION make ${PLATFORM} install mib510,${PPORT} -fi +CFLAGS=-DDELUGE_BASESTATION make ${PLATFORM} install ${PPORT} echo ' +-------------------------------------------------------+' @@ -74,7 +59,7 @@ echo ============================= Compile a new Blink ======================== CFLAGS=-DBLINK_REVERSE\ -DDELUGE_BASESTATION make ${PLATFORM} echo =============================== Upload the image =============================== -${TOS_DELUGE} ${CPORT} ${PLATFORM} -i 1 build/${PLATFORM}/tos_image.xml +${TOS_DELUGE} ${CPORT} -i 1 build/${PLATFORM}/tos_image.xml echo ' +----------------------------------------------------------------+' echo ' | |' @@ -91,4 +76,4 @@ echo ' +----------------------------------------------------------------+' read echo =========================== Reboot the base station ============================ -${TOS_DELUGE} ${CPORT} ${PLATFORM} -r 1 +${TOS_DELUGE} ${CPORT} -r 1 diff --git a/apps/tests/deluge/Blink/burn-net b/apps/tests/deluge/Blink/burn-net index cc146661..8c0bc609 100755 --- a/apps/tests/deluge/Blink/burn-net +++ b/apps/tests/deluge/Blink/burn-net @@ -5,6 +5,14 @@ if [[ ! -x ${TOS_DELUGE} ]] ; then TOS_DELUGE=../../../../tools/tinyos/misc/tos-deluge fi +$TOS_DELUGE > /dev/null + +if [[ $? != 0 ]] +then + echo "Unable to locate tos-deluge." + exit 2 +fi + if [ $# -ne 2 ]; then echo "Usage: $0 " echo " micaz, telosb or iris" @@ -15,7 +23,11 @@ fi PLATFORM=$1 NO_MOTES=$2 -if [ ${PLATFORM} != 'micaz' -a ${PLATFORM} != 'telosb' -a ${PLATFORM} != 'iris' ]; then +if [ ${PLATFORM} != 'micaz' -a \ + ${PLATFORM} != 'telosb' -a \ + ${PLATFORM} != 'iris' \ + ${PLATFORM} != 'epic' ] +then echo "\"${PLAFTORM}\" is not a supported platform" exit 2 fi @@ -32,19 +44,10 @@ ID=0 function burn_one() { ID=`expr $ID + 1` - echo -n ">>> Please plug mote $ID and type the programming port to continue: " + echo -n ">>> Please plug mote $ID and type the programming sorce (bsl,PORT | mib510,PORT | eprb,PORT): " read PORT - if [ ${PLATFORM} == 'micaz' ] - then - CFLAGS=$1 make ${PLATFORM} install,$ID mib510,${PORT} - elif [ ${PLATFORM} == 'telosb' ] - then - CFLAGS=$1 make ${PLATFORM} install,$ID bsl,${PORT} - elif [ ${PLATFORM} == 'iris' ] - then - CFLAGS=$1 make ${PLATFORM} install,$ID mib510,${PORT} - fi + CFLAGS=$1 make ${PLATFORM} install,$ID ${PORT} } while [[ ${NO_MOTES} > 1 ]] @@ -54,9 +57,8 @@ do done echo ">>> Note: this last mote will be the basestation! <<<" burn_one -DDELUGE_BASESTATION -echo -n ">>> Please plug mote $ID and type the communication port to continue: " -read PORT -BASESTATION_PORT=$PORT +echo -n ">>> Please plug mote $ID and type the communication sorce (serial@PORT:SPEED | network@HOST:PORT) to continue: " +read CPORT echo ' +------------------------------------------------------------------------+' echo ' | |' @@ -71,7 +73,7 @@ echo ============================= Compile a new Blink ========================= CFLAGS=-DBLINK_REVERSE\ -DDELUGE_LIGHT_BASESTATION make ${PLATFORM} echo ========= Upload the new image to the external flash of the last mote ========== -${TOS_DELUGE} ${BASESTATION_PORT} ${PLATFORM} -i 1 build/${PLATFORM}/tos_image.xml +${TOS_DELUGE} ${CPORT} -i 1 build/${PLATFORM}/tos_image.xml echo ' +-----------------------------------------------------+' echo ' | |' @@ -85,7 +87,7 @@ echo ' +-----------------------------------------------------+' read echo ============================= Start dissemination ============================== -${TOS_DELUGE} ${BASESTATION_PORT} ${PLATFORM} -dr 1 +${TOS_DELUGE} ${CPORT} -dr 1 echo ' +------------------------------------------------------------+' echo ' | |' diff --git a/support/sdk/python/tos.py b/support/sdk/python/tos.py index 7aaf760c..85f283b7 100644 --- a/support/sdk/python/tos.py +++ b/support/sdk/python/tos.py @@ -21,17 +21,21 @@ # @author Razvan Musaloiu-E. # @author David Purdy -"""A library that implements the T2 serial communication. +""" +A library that implements the T2 serial communication. This library has two parts: one that deals with sending and receiving packets using the serial format from T2 (TEP113) and a second one that tries to simplifies the work with arbitrary packets. - """ -import sys, struct, time, serial, socket, operator, thread -from Queue import Queue -from threading import Lock, Condition +import sys, struct, time, socket, operator, os + +try: + import serial +except ImportError, e: + print "Please install PySerial first." + sys.exit(1) __version__ = "$Id$" @@ -40,124 +44,121 @@ __all__ = ['Serial', 'AM', 'AckFrame', 'DataFrame', 'NoAckDataFrame', 'ActiveMessage'] -ACK_WAIT = 0.2 # Maximum amount of time to wait for an ack -ACK_WARN = 0.2 # Warn if acks take longer than this to arrive - -def list2hex(v): - return " ".join(["%02x" % p for p in v]) - - -class Error(Exception): - """Base error class for this module""" - pass - - -class TimeoutError(Error): - """Thrown when a serial operation times out""" - pass - +HDLC_FLAG_BYTE = 0x7e +HDLC_CTLESC_BYTE = 0x7d -class ReadError(Error): - """Base class for read error exceptions""" - pass - - -class WriteError(Error): - """Base class for write error exceptions""" - pass - - -class ReadTimeoutError(TimeoutError, ReadError): - """Thrown when a serial read operation times out""" - pass +TOS_SERIAL_ACTIVE_MESSAGE_ID = 0 +TOS_SERIAL_CC1000_ID = 1 +TOS_SERIAL_802_15_4_ID = 2 +TOS_SERIAL_UNKNOWN_ID = 255 +SERIAL_PROTO_ACK = 67 +SERIAL_PROTO_PACKET_ACK = 68 +SERIAL_PROTO_PACKET_NOACK = 69 +SERIAL_PROTO_PACKET_UNKNOWN = 255 -class ReadCRCError(ReadError): - """Thrown when a read packet fails a CRC check""" - pass - - -class BadAckSeqnoError(ReadError): - """Thrown if an ack packet has an unexpected sequenc number""" - pass - +def list2hex(v): + return " ".join(["%02x" % p for p in v]) -class WriteTimeoutError(TimeoutError, WriteError): - """Thrown when a serial write operation times out""" +class Timeout(Exception): pass +def getSource(comm): + source = comm.split('@') + params = source[1].split(':') + if source[0] == 'serial': + try: + return Serial(params[0], int(params[1]), flush=True, debug=False) + except: + print "ERROR: Unable to initialize serial port connection to", comm + sys.exit(-1) + elif source[0] == 'network': + try: + return SerialMIB600(params[0], int(params[1]), debug=False) + except: + print "ERROR: Unable to initialize serial port connection to", comm + sys.exit(-1) + raise Exception -class SimpleSerial: - """ - A SimpleSerial object offers a way to send and data using a HDLC-like - formating. - - Use SimpleSerial objects for basic low-level serial communications. Use - Serial objects for higher level logic (retry sends, log printfs, etc). - """ - - HDLC_FLAG_BYTE = 0x7e - HDLC_CTLESC_BYTE = 0x7d - - TOS_SERIAL_ACTIVE_MESSAGE_ID = 0 - TOS_SERIAL_CC1000_ID = 1 - TOS_SERIAL_802_15_4_ID = 2 - TOS_SERIAL_UNKNOWN_ID = 255 - - SERIAL_PROTO_ACK = 67 - SERIAL_PROTO_PACKET_ACK = 68 - SERIAL_PROTO_PACKET_NOACK = 69 - SERIAL_PROTO_PACKET_UNKNOWN = 255 - - def __init__(self, port, baudrate, flush=False, debug=False, qsize=10, - timeout=None): - self._debug = debug - self._in_queue = [] - self._qsize = qsize - self._ack = None - self._write_counter = 0 - self._write_counter_failures = 0 - self._read_counter = 0 +class Serial: + def __init__(self, port, baudrate, flush=False, debug=False, timeout=None): + self.debug = debug + self.timeout = timeout self._ts = None - self.timeout = timeout # Public attribute - self._received_packet_filters = [] # filter functions for received packets - - # Remember sent (and unacknowledged) seqno numbers for 15 seconds: - self._unacked_seqnos = SeqTracker(15.0) - self._s = serial.Serial(port, baudrate, rtscts=0, timeout=0.5) + self._s = serial.Serial(port, int(baudrate), rtscts=0, timeout=0.5) self._s.flushInput() if flush: print >>sys.stdout, "Flushing the serial port", endtime = time.time() + 1 while time.time() < endtime: - try: - self._read() - except ReadError: - pass + self._s.read() sys.stdout.write(".") - if not self._debug: + if not self.debug: sys.stdout.write("\n") self._s.close() self._s = serial.Serial(port, baudrate, rtscts=0, timeout=timeout) - # Add a filter for received 'write ack' packets - self.add_received_packet_filter(self._write_ack_filter) + def getByte(self): + c = self._s.read() + if c == '': + raise Timeout + #print 'Serial:getByte: 0x%02x' % ord(c) + return ord(c) - # Returns the next incoming serial packet - def _read(self, timeout=None): - """Wait for a packet and return it as a RawPacket. + def putBytes(self, data): + #print "DEBUG: putBytes:", data + for b in data: + self._s.write(struct.pack('B', b)) - Throws: - - ReadCRCError if a CRC check fails - - ReadTimeoutError if the timeout expires. + def getTimeout(self): + return self._s.timeout - """ + def setTimeout(self, timeout): + self._s.timeout = timeout + +class SerialMIB600: + def __init__(self, host, port=10002, debug=False): + self.debug = debug + self._ts = None + self._s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._s.connect((host, port)) + print "Connected" + + def getByte(self): + c = self._s.recv(1) + if c == '': + raise Timeout + #print 'Serial:getByte: 0x%02x' % ord(c) + return ord(c) + + def putBytes(self, data): + #print "DEBUG: putBytes:", data + for b in data: + self._s.send(struct.pack('B', b)) + + def getTimeout(self): + return self._s.gettimeout() + + def setTimeout(self, timeout): + self._s.settimeout(timeout) + +class HDLC: + """ + An HDLC object offers a way to send and receive data on a byte + source using a HDLC-like formating. + """ + def __init__(self, source): + self._s = source + + # Returns the next incoming serial packet + def read(self, timeout=None): + """Wait for a packet and return it as a RawPacket.""" # Developer notes: # - # Packet data read from Serial is in this format: - # [HDLC_FLAG_BYTE][Escaped data][HDLC_FLAG_BYTE] + # Packet data read is in this format: + # [HDLC_FLAG_BYTE][Escaped data][HDLC_FLAG_BYTE] # # [Escaped data] is encoded so that [HDLC_FLAG_BYTE] byte # values cannot occur within it. When [Escaped data] has been @@ -167,57 +168,72 @@ class SimpleSerial: # # It's also possible that the serial device was half-way # through transmitting a packet when this function was called - # (app was just started). So we also neeed to handle this case: + # (app was just started). So we also neeed to handle this + # case: # - # [Incomplete escaped data][HDLC_FLAG_BYTE][HDLC_FLAG_BYTE][Escaped data][HDLC_FLAG_BYTE] + # [Incomplete escaped data][HDLC_FLAG_BYTE][HDLC_FLAG_BYTE][Escaped data][HDLC_FLAG_BYTE] # # In this case we skip over the first (incomplete) packet. # - if self._s.timeout != timeout and timeout != None: - if self._debug: - print "Set the timeout to %s, previous one was %s" % (timeout, self._s.timeout) - self._s.timeout = timeout + if self._s.getTimeout() != timeout and timeout != None: + self.log("Set the timeout to %s, previous one was %s" % (timeout, self._s.getTimeout())) + self._s.setTimeout(timeout) + + # +--- FLAG -----+ + # | | ___________ + # v | / | + # >(1)-- !FLAG -->(2)<-- !FLAG --+ + # | + # FLAG + # | ___________ + # v / | + # (3)<-- FLAG ---+ + # | + # !FLAG + # | ___________ + # v / | + # (4)<-- !FLAG --+ + # | + # FLAG + # | + # v + # (5) try: # Read bytes until we get to a HDLC_FLAG_BYTE value # (either the end of a packet, or the start of a new one) - d = self._get_byte(timeout) + d = self._s.getByte() ts = time.time() - if self._debug and d != self.HDLC_FLAG_BYTE: - print "Skipping incomplete packet" - while d != self.HDLC_FLAG_BYTE: - d = self._get_byte(timeout) - ts = time.time() + if d != HDLC_FLAG_BYTE: + self.log("Skipping byte %d" % d) + while d != HDLC_FLAG_BYTE: + d = self._s.getByte() + self.log("Skipping byte %d" % d) + ts = time.time() # Store HDLC_FLAG_BYTE at the start of the retrieved packet # data: packet = [d] # Is the next byte also HDLC_FLAG_BYTE? - d = self._get_byte(timeout) - if d == self.HDLC_FLAG_BYTE: - # Yes. This means that the previous byte was for - # the end of the previous packet, and this byte is for - # the start of the next packet. - - # Get the 2nd byte of the new packet: - d = self._get_byte(timeout) + d = self._s.getByte() + while d == HDLC_FLAG_BYTE: + d = self._s.getByte() ts = time.time() # We are now on the 2nd byte of the packet. Add it to # our retrieved packet data: packet.append(d) - # Read bytes from serial until we read another - # HDLC_FLAG_BYTE value (end of the current packet): - while d != self.HDLC_FLAG_BYTE: - d = self._get_byte(timeout) + # Read bytes from serial until we read another HDLC_FLAG_BYTE + # value (end of the current packet): + while d != HDLC_FLAG_BYTE: + d = self._s.getByte() packet.append(d) # Done reading a whole packet from serial - if self._debug: - print "SimpleSerial:_read: unescaped", packet + self.log("SimpleSerial:_read: unescaped %s" % packet) # Decode the packet, and check CRC: packet = self._unescape(packet) @@ -227,112 +243,17 @@ class SimpleSerial: if crc != packet_crc: print "Warning: wrong CRC! %x != %x %s" % (crc, packet_crc, ["%2x" % i for i in packet]) - raise ReadCRCError - if self._debug: - if self._ts == None: - self._ts = ts - else: - print "Serial:_read: %.4f (%.4f) Recv:" % (ts, ts - self._ts), self._format_packet(packet[1:-3]) - self._ts = ts + if not self._s._ts: + self._s._ts = ts + self.log("Serial:_read: %.4f (%.4f) Recv: %s" % (ts, ts - self._s._ts, self._format(packet[1:-3]))) + self._ts = ts # Packet was successfully retrieved, so return it in a - # RawPacket wrapper object (but leave out the - # HDLC_FLAG_BYTE and CRC bytes) + # RawPacket wrapper object (but leave out the HDLC_FLAG_BYTE + # and CRC bytes) return RawPacket(ts, packet[1:-3]) - except socket.timeout: - raise ReadTimeoutError - - def _write_ack_filter(self, packet): - """Filter for recieved write acknowledgement packets""" - ack = AckFrame(packet.data) - if ack.protocol == self.SERIAL_PROTO_ACK: - if self._debug: - print "_filter_read: got an ack:", ack - self._ack = ack - packet = None # No further processing of received ack packet - return packet - - def _filter_read(self, timeout=None): - """Read a packet from the serial device, perform filtering, and return - the packet if it hasn't been processed yet. - - """ - p = self._read(timeout) - self._read_counter += 1 - if self._debug: - print "_filter_read: got a packet(%d): %s" % (self._read_counter, p) - - # Pass the received packet through the filter functions: - if p is not None: - for filter_func in self._received_packet_filters: - p = filter_func(p) - # Stop now if the packet doesn't need further processing: - if p is None: - break - - # Return the packet (if there was no timeout and it wasn't filtered) - return p - - def _get_ack(self, timeout, expected_seqno): - """Get the next ack packet - - Read packets from the serial device until we get the next ack (which - then gets stored in self._ack), or the timeout expires. non-ack packets - are buffered. - - Throws: - - ReadTimeoutError if the timeout expires. - - BadAckSeqnoError if an ack with a bad sequence number is received - - """ - endtime = time.time() + timeout - while time.time() < endtime: - # Read the a packet over serial - self._ack = None - remaining = endtime - time.time() - p = self._filter_read(timeout) - - # Was the packet filtered? - if p: - # Got an unfiltered packet - if len(self._in_queue) >= self._qsize: - print "Warning: Buffer overflow" - self._in_queue.pop(0) - self._in_queue.append(p) - else: - # Packet was filtered. Was it an ack? - if self._ack is not None: - # The packet was an ack, so remove it from our - # 'unacknowledged seqnos' list (or raise a BadAckSeqnoError - # error if it isn't in the list) - self._unacked_seqnos.seqno_acked(self._ack.seqno) - - # Stop reading packets if it's the ack we are waiting for: - if self._ack.seqno == expected_seqno: - return - - # Timed out - raise ReadTimeoutError - - def close(self): - """Close the serial device""" - self._s.close() - - def read(self, timeout=None): - """Read a packet, either from the input buffer or from the serial - device. - - Returns a RawPacket object, otherwise None if the packet was filtered - (by eg: Serial's printf-filtering function) - - Does not retry reads if the first one fails. Use Serial.read() for - that. - - """ - if self._in_queue: - return self._in_queue.pop(0) - else: - return self._filter_read(timeout) + except Timeout: + return None def write(self, payload, seqno, timeout=0.2): """ @@ -340,24 +261,14 @@ class SimpleSerial: assumed to be exactly the payload. Otherwise the payload is assume to be a Packet and the real payload is obtain by calling the .payload(). - - Only attempts to write once, and times out if an ack packet is not - received within [timeout] seconds. Use Serial.write() if you want - automatic write retries. - - seqno should be an integer between 0 and 99 which changes each time you - send a new packet. The value should remain the same when you are - retrying a packet write that just failed. - - Raises WriteTimeoutError if the write times out (ack packet doesn't - arrive within [timeout] seconds). - """ - if type(payload) != type([]): - # Assume this will be derived from Packet + + if isinstance(payload, Packet): payload = payload.payload() + packet = DataFrame(); - packet.protocol = self.SERIAL_PROTO_PACKET_ACK + # We need to always request for acks + packet.protocol = SERIAL_PROTO_PACKET_ACK packet.seqno = seqno packet.dispatch = 0 packet.data = payload @@ -365,59 +276,14 @@ class SimpleSerial: crc = self._crc16(0, packet) packet.append(crc & 0xff) packet.append((crc >> 8) & 0xff) - packet = [self.HDLC_FLAG_BYTE] + self._escape(packet) + [self.HDLC_FLAG_BYTE] - - # Write the packet: - self._unacked_seqnos.seqno_sent(seqno) # Keep track of sent seqno's - self._put_bytes(packet) - self._write_counter += 1 - - # Wait for an ack packet: - if self._debug: - print "Send(%d/%d): %s" % (self._write_counter, self._write_counter_failures, packet) - print "Wait for ack %d ..." % (seqno) - - try: - self._get_ack(timeout, seqno) - except ReadTimeoutError: - # Re-raise read timeouts (of ack packets) as write timeouts (of - # the write operation) - self._write_counter_failures += 1 - raise WriteTimeoutError - - # Received an ack packet, with the expected sequence number - if self._debug: - print "Wait for ack %d done. Latest ack:" % (seqno), self._ack - print "The packet was acked." - print "Returning from SimpleSerial.write..." - - def add_received_packet_filter(self, filter_func): - """Register a received packet-filtering callback function - - _filter_read() calls all of the registered filter functions for each - packet received over serial. Registered filter functions are called in - the order they were registered. + packet = [HDLC_FLAG_BYTE] + self._escape(packet) + [HDLC_FLAG_BYTE] - Filter functions are called like this: filter_func(packet) + self.log("Serial: write %s" % packet) + self._s.putBytes(packet) - When a filter function recognises and handles a received packet it - should return a None value to indicate that no further processing - is required for the packet. - - When a filter function skips a packet (or for some reason you want - further processing to happen on a packet you've just processed), the - function should return the packet that was passed to it as an argument. - - """ - self._received_packet_filters.append(filter_func) - - def remove_received_packet_filter(self, filter_func): - """Remove a filter function added with add_received_packet_filter()""" - self._received_packet_filters.remove(filter_func) - - def _format_packet(self, payload): + def _format(self, payload): f = NoAckDataFrame(payload) - if f.protocol == self.SERIAL_PROTO_ACK: + if f.protocol == SERIAL_PROTO_ACK: rpacket = AckFrame(payload) return "Ack seqno: %d" % (rpacket.seqno) else: @@ -452,24 +318,6 @@ class SimpleSerial: r = (r << 8) + i return r - def _get_byte(self, timeout=None): -# old_timeout = self._s.timeout -# if timeout is not None: -# self._s.timeout = timeout - try: - r = struct.unpack("B", self._s.read())[0] - return r - except struct.error: - # Serial port read timeout - raise socket.timeout -# finally: -# self._s.timeout = old_timeout - - def _put_bytes(self, data): - #print "DEBUG: _put_bytes:", data - for b in data: - self._s.write(struct.pack('B', b)) - def _unescape(self, packet): r = [] esc = False @@ -477,7 +325,7 @@ class SimpleSerial: if esc: r.append(b ^ 0x20) esc = False - elif b == self.HDLC_CTLESC_BYTE: + elif b == HDLC_CTLESC_BYTE: esc = True else: r.append(b) @@ -486,285 +334,140 @@ class SimpleSerial: def _escape(self, packet): r = [] for b in packet: - if b == self.HDLC_FLAG_BYTE or b == self.HDLC_CTLESC_BYTE: - r.append(self.HDLC_CTLESC_BYTE) + if b == HDLC_FLAG_BYTE or b == HDLC_CTLESC_BYTE: + r.append(HDLC_CTLESC_BYTE) r.append(b ^ 0x20) else: r.append(b) return r - def debug(self, debug): - self._debug = debug - - -class SeqTracker: - """Class for keeping track of unacknowledged packet sequence numbers. - - SeqTracker is used by SimpleSerial to keep track of sequence numbers which - have been sent with write packets, but not yet acknowledged by received - write ack packets. + def log(self, s): + if self._s.debug: + print s - """ - def __init__(self, keep_for): - """Initialise a SeqTracker object. - - args: - - - keep_for is the length of time for which unacknowledged sequence - numbers should be remembered. After this period has elapsed, the - sequence numbers should be forgotten. If the sequence number is - acknowledged later, it will be treated as unkown - - """ - self._keep_for = keep_for - self._queue = [] - - def seqno_sent(self, seqno): - """Register that a packet with the specified sequence number was just - sent.""" - self._gc() - self._queue.append((seqno, time.time())) - - def seqno_acked(self, seqno): - """Register that a sequence number was just acknowledged. +class SimpleAM(object): + def __init__(self, s, oobHook=None): + self._s = HDLC(s) + self.seqno = 0 + self.oobHook = oobHook - Find the oldest-known occurance of seqno in the queue and remove it. If - not found then raise a BadAckSeqnoError to inform applications that - the sequence number is not known. + def read(self, timeout=None): + f = self._s.read(timeout) + if f: + return ActiveMessage(NoAckDataFrame(f)) + return None - """ - self._gc() - for item in self._queue: - if item[0] == seqno: - # Found seqno - self._queue.remove(item) - return - # seqno not found! - raise BadAckSeqnoError - - def get_seqno_sent_times(self, seqno): - """Return the times when packets with the given sequence number were - sent.""" - self._gc() - return [item[1] for item in self._queue if item[0] == seqno] - - def __contains__(self, seqno): - """Return True if the seqno was sent recently (and not acknowledged - yet)""" - self._gc() - for item in self._queue: - if item[0] == seqno: + def write(self, packet, amId, timeout=None, blocking=True, inc=1): + self.seqno = (self.seqno + inc) % 256 + while True: + self._s.write(ActiveMessage(packet, amId=amId), seqno=self.seqno, timeout=timeout) + if not blocking: return True - return False - - def _gc(self): - """Remove old items from the queue""" - remove_before = time.time() - self._keep_for - for item in self._queue: - # Time for the sequence to be removed? - if item[1] < remove_before: - # Sequence data is old, so remove it - self._queue.remove(item) - else: - # Sequence number was added recently, so don't remove it. Also - # stop processing the queue because all later items will be - # newer + f = self._s.read(timeout) + if f == None: + continue + ack = AckFrame(f) + while ack.protocol != SERIAL_PROTO_ACK: + if self.oobHook: + self.oobHook(ActiveMessage(NoAckDataFrame(f))) + else: + print 'SimpleAM:write: skip', ack, f + f = self._s.read(timeout) + if f == None: + break + ack = AckFrame(f) + if f != None: break - - -class Serial: - """ - Wraps a SimpleSerial object, and provides some higher-level functionality - like retrying writes and logging printf packets. - """ - def __init__(self, port, baudrate, flush=False, debug=False, qsize=10, - timeout=None): - """Initialise a Serial object""" - self._debug = debug - self.timeout = timeout # Public attribute - self._seqno = 0 - self._simple_serial = SimpleSerial(port, baudrate, flush, debug, qsize, - timeout) - - # Setup automatic logging of received printf packets: - self._printf_msg = "" - self._simple_serial.add_received_packet_filter(self._printf_filter) - - def close(self): - """Close the serial device""" - self._simple_serial.close() - - def read(self, timeout=None): - """Read a packet from the serial port. - - Retries packet reads until the timeout expires. - - Throws ReadTimeoutError if a a packet can't be read within the timeout. - - """ - if timeout is None: - timeout = self.timeout - endtime = None - - if timeout is not None: - endtime = time.time() + timeout - - while endtime is None or time.time() < endtime: - remaining = None - if endtime is not None: - remaining = endtime - time.time() + #print 'SimpleAM:write: got an ack:', ack, ack.seqno == self.seqno + return ack.seqno == self.seqno + + def setOobHook(self, oobHook): + self.oobHook = oobHook + +def printfHook(packet): + if packet == None: + return + if packet.type == 100: + s = "".join([chr(i) for i in packet.data]).strip('\0') + lines = s.split('\n') + for line in lines: + print "PRINTF:", line + packet = None # No further processing for the printf packet + return packet + +class AM(SimpleAM): + def __init__(self, s=None, oobHook=None): + if s == None: try: - p = self._simple_serial.read(remaining) - except ReadError: - if self._debug: - print "Packet read failed. Try again." - else: - # Was the packet filtered? - if p is not None: - # Not filtered, so return it. - # In the current TinyOS the packets from the mote are - # always NoAckDataFrame - return NoAckDataFrame(p.data) - - # Read timeout expired - raise ReadTimeoutError - - def write(self, payload, timeout=None): - """Write a packet to the serial port - - Keeps retrying endlessly, unless a timeout is set. If the timeout - expires then WriteTimeoutError is thrown. - - """ - if timeout is None: - timeout = self.timeout - - endtime = None - if timeout is not None: - endtime = time.time() + timeout - - # Generate the next sequence number: - self._seqno = (self._seqno + 1) % 100 - - while endtime is None or time.time() < endtime: - try: - ackwait = ACK_WAIT - if endtime is not None: - remaining = endtime - time.time() - ackwait = min(ACK_WAIT, remaining) - - before = time.time() - self._simple_serial.write(payload, self._seqno, ackwait) - length = time.time() - before - - if length >= ACK_WARN: - print "Warning: Packet write took %.3fs!" % (length) - return True - except Error: - if self._debug: - print "The packet was not acked. Try again." - - # Write operation timed out - raise WriteTimeoutError - - def _printf_filter(self, packet): - """Filter for recieved printf packets""" - ampkt = ActiveMessage(NoAckDataFrame(packet.data).data) - if ampkt.type == 100: - self._printf_msg += "".join([chr(i) for i in ampkt.data]).strip('\0') - # Split printf data on newline character: - # (last string in the split list doesn't have a newline after - # it, so we keep it until next time) - lines = self._printf_msg.split('\n') - for line in lines[:-1]: - print "PRINTF:", line - self._printf_msg = lines[-1] - packet = None # No further processing for the printf packet - return packet - -class SFClient: - def __init__(self, host, port, qsize=10): - self._in_queue = Queue(qsize) - self._s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self._s.connect((host, port)) - data = self._s.recv(2) - if data != 'U ': - print "Wrong handshake" - self._s.send("U ") - print "Connected" - thread.start_new_thread(self.run, ()) - - def run(self): - while True: - length = ord(self._s.recv(1)) - data = self._s.recv(length) - data = [ord(c) for c in data][1:] - #print "Recv %d bytes" % (length), ActiveMessage(data) - if self._in_queue.full(): - print "Warning: Buffer overflow" - self._in_queue.get() - p = RawPacket() - p.data = data - self._in_queue.put(p, block=False) - - def read(self, timeout=0): - return self._in_queue.get() - - def write(self, payload): - print "SFClient: write:", payload - if type(payload) != type([]): - # Assume this will be derived from Packet - payload = payload.payload() - payload = [0] + payload - self._s.send(chr(len(payload))) - self._s.send(''.join([chr(c) for c in payload])) - return True - -class AM: - def __init__(self, s): - self._s = s + s = getSource(sys.argv[1]) + except: + try: + for (i, j) in zip(sys.argv[1::2], sys.argv[2::2]): + if i == '-comm': + s = getSource(j) + if s == None: + raise Exception + except: + try: + s = getSource(os.environ['MOTECOM']) + print 'third' + except: + print "ERROR: please indicate a way to connect to the mote" + sys.exit(-1) + if oobHook == None: + oobHook = printfHook + super(AM, self).__init__(s, oobHook) def read(self, timeout=None): - return ActiveMessage(self._s.read(timeout).data) - - def write(self, packet, amid, timeout=None): - return self._s.write(ActiveMessage(packet, amid=amid), timeout=timeout) - - -class SimpleSerialAM(SimpleSerial): - """A derived class of SimpleSerial so that apps can read and write using - higher-level packet structures. + return self.oobHook(super(AM, self).read(timeout)) - Serves a simalar purpose to the AM class, but for SimpleSerial objects - instead instead of Serial. - - """ - - def read_am(self, timeout=None): - """Read a RawPacket object (or None), convert it to ActiveMessage - (or None), and return to the caller""" - - # Get a tos.Rawpacket (or None, if filtered) object - p = self.read(timeout) - if p is not None: - assert isinstance(p, RawPacket) - # Convert tos.RawPacket object into an ActiveMessage: - p = NoAckDataFrame(p.data) - p = ActiveMessage(p.data) - - # Return the ActiveMessage (or None) packet: - return p - - def write_am(self, packet, amid, seqno, timeout=2.0): - """Convert app packet format to ActiveMessage, and write the - ActiveMessage packet to serial""" - - # Convert from app-specific packet to ActiveMessage: - p = ActiveMessage(packet, amid=amid) + def write(self, packet, amId, timeout=None, blocking=True): + r = super(AM, self).write(packet, amId, timeout, blocking) + while not r: + r = super(AM, self).write(packet, amId, timeout, blocking, inc=0) + return True - # Write to the serial device - self.write(p, seqno, timeout) +# class SFClient: +# def __init__(self, host, port, qsize=10): +# self._in_queue = Queue(qsize) +# self._s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +# self._s.connect((host, port)) +# data = self._s.recv(2) +# if data != 'U ': +# print "Wrong handshake" +# self._s.send("U ") +# print "Connected" +# thread.start_new_thread(self.run, ()) + +# def run(self): +# while True: +# length = ord(self._s.recv(1)) +# data = self._s.recv(length) +# data = [ord(c) for c in data][1:] +# #print "Recv %d bytes" % (length), ActiveMessage(data) +# if self._in_queue.full(): +# print "Warning: Buffer overflow" +# self._in_queue.get() +# p = RawPacket() +# p.data = data +# self._in_queue.put(p, block=False) + +# def read(self, timeout=0): +# return self._in_queue.get() + +# def write(self, payload): +# print "SFClient: write:", payload +# if type(payload) != type([]): +# # Assume this will be derived from Packet +# payload = payload.payload() +# payload = [0] + payload +# self._s.send(chr(len(payload))) +# self._s.send(''.join([chr(c) for c in payload])) +# return True + + +################################################################################ class Packet: """ @@ -786,6 +489,11 @@ class Packet: output.reverse() return output + def _sign(self, val, dim): + if val > (1 << (dim * 8 - 1)): + return val - (1 << (dim * 8)) + return val + def __init__(self, desc, packet = None): offset = 0 boffset = 0 @@ -805,6 +513,9 @@ class Packet: if t == 'int': self._values.append(self._decode(packet[offset:offset + s])) offset += s + elif t == 'sint': + self._values.append(self._sign(self._decode(packet[offset:offset + s]), s)) + offset += s elif t == 'bint': doffset = 8 - (boffset + s) self._values.append((packet[offset] >> doffset) & ((1< <-p|-i|-r|-d|-e|-s> image_number [options]" % sys.argv[0] - print " Either the platform name (micaz or telosv) or a baudrate value" + print "Usage: %s <-p|-i|-r|-d|-e|-s> image_number [options]" % sys.argv[0] + print " can be:" + print " serial@PORT:SPEED Serial ports" + print " network@HOST:PORT MIB600" print " -p --ping Provide status of the image in the external flash" print " -i --inject Inject a compiled TinyOS application" print " [options]: tos_image.xml file path" @@ -391,71 +389,55 @@ def checkImgNum(): global imgNum # Checks for valid image number format try: - imgNum = int(sys.argv[4]) + imgNum = int(sys.argv[3]) except: print "ERROR: Image number is not valid" sys.exit(-1) return imgNum # ======== MAIN ======== # -if len(sys.argv) >= 4: +if len(sys.argv) >= 3: - if sys.argv[2] in BAUDRATES: - baudrate = BAUDRATES[sys.argv[2]] - else: - try: - baudrate = int(sys.argv[2]) - except: - print "ERROR: Wrong baudrate" - sys.exit(-1) - - # Initializes serial port communication - try: - s = tos.Serial(sys.argv[1], baudrate, flush=True, debug=False) - am = tos.AM(s) - except: - print "ERROR: Unable to initialize serial port connection to", sys.argv[1] - sys.exit(-1) + am = tos.AM() - # Check if the mote has the Deluge T2 basestation component: try: print "Checking if node is a Deluge T2 base station ..." ident(timeout=5) - except tos.TimeoutError: + except tos.Timeout: print "ERROR: Timeout. Is the node a Deluge T2 base station?" sys.exit(-1) - if sys.argv[3] in ["-p", "--ping"]: + if sys.argv[2] in ["-p", "--ping"]: checkImgNum() print "Pinging node ..." ping(imgNum) - elif sys.argv[3] in ["-i", "--inject"] and len(sys.argv) == 6: + elif sys.argv[2] in ["-i", "--inject"] and len(sys.argv) == 5: checkImgNum() print "Pinging node ..." - inject(imgNum, sys.argv[5]) - elif sys.argv[3] in ["-e", "--erase"]: + inject(imgNum, sys.argv[4]) + elif sys.argv[2] in ["-e", "--erase"]: checkImgNum() if erase(imgNum): print "Image number %d erased" % imgNum - elif sys.argv[3] in ["-b", "--boot"]: + elif sys.argv[2] in ["-b", "--boot"]: if boot(): print "Command sent" - elif sys.argv[3] in ["-r", "--reprogram"]: + elif sys.argv[2] in ["-r", "--reprogram"]: checkImgNum() if reprogram(imgNum): print "Command sent" - elif sys.argv[3] in ["-d", "--disseminate"]: + elif sys.argv[2] in ["-d", "--disseminate"]: checkImgNum() if disseminate(imgNum): print "Command sent" - elif sys.argv[3] in ["-dr", "--disseminate-and-reboot"]: + elif sys.argv[2] in ["-dr", "--disseminate-and-reboot"]: checkImgNum() if disseminateAndReboot(imgNum): print "Command sent" - elif sys.argv[3] in ["-s", "--stop"]: + elif sys.argv[2] in ["-s", "--stop"]: if stop(): print "Command sent" - elif sys.argv[3] in ["-ls", "--local-stop"]: + elif sys.argv[2] in ["-ls", "--local-stop"]: if localstop(): print "Command sent" else: diff --git a/tools/tinyos/misc/tos-dump.py b/tools/tinyos/misc/tos-dump.py new file mode 100644 index 00000000..1d669e1c --- /dev/null +++ b/tools/tinyos/misc/tos-dump.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +import sys +import tos + +if '-h' in sys.argv: + print "Usage:", sys.argv[0], "serial@/dev/ttyUSB0:57600" + print " ", sys.argv[0], "network@host:port" + sys.exit() + +am = tos.AM() + +while True: + p = am.read() + if p: + print p + -- 2.39.2