[Tinyos-commits] CVS: tinyos-1.x/tools/python/pytos/util JavaInheritor.py, NONE, 1.1 KeyPress.py, NONE, 1.1 NescApp.py, NONE, 1.1 RoutingMessages.py, NONE, 1.1 SupplementQuery.py, NONE, 1.1 Timer.py, NONE, 1.1 __init__.py, NONE, 1.1 nescDecls.py, NONE, 1.1

Kamin Whitehouse kaminw at users.sourceforge.net
Fri Sep 23 03:20:36 PDT 2005


Update of /cvsroot/tinyos/tinyos-1.x/tools/python/pytos/util
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv5850/pytos/util

Added Files:
	JavaInheritor.py KeyPress.py NescApp.py RoutingMessages.py 
	SupplementQuery.py Timer.py __init__.py nescDecls.py 
Log Message:
pytos is a python-based toolchain for tinyos with a dynamic typing system.  it automatically imports the enums, types, message formats, modules, variables, and rpc functions from applications that are compiled with the 'rpc' and 'nescDecls' targets.

--- NEW FILE: JavaInheritor.py ---
# "Copyright (c) 2000-2003 The Regents of the University of California.  
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose, without fee, and without written agreement
# is hereby granted, provided that the above copyright notice, the following
# two paragraphs and the author appear in all copies of this software.
# 
# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
# OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
#
# @author Kamin Whitehouse 
#

import re
from jpype import jimport

class JavaInheritor ( object ) :
    """This python class should usually be inherited from.  It has one
    field called self.javaParents which should be a list of java
    objects, and this python class \"inherits\" all fields and
    functions of those java objects.  If two java objects have the
    same field or function, the one from the first object in the list
    is used.  The fields and functions, of course, can be overridden
    in the derived python class.

    At the end of the constructor of the derived python class, the
    constructor of this class MUST be called as follows:
        JavaInheritor.__init__(self, javaParents)
    """

    def __init__(self, javaParents):
        self._inheritedFields = {}
        self._fullClassnames = []
        self._inheritedFieldNames = []
        self._javaParents = javaParents
        classmatch = re.compile('^<jpype\\._jclass\\.(.*\\.(.+?)) ')
        pythonfield = re.compile('__.+__')
        allFieldNames = set(dir(self)) | \
                        set(dir(jimport.java.lang.Object))
        for parent in self._javaParents :
            fieldNames = set(dir(parent)) - allFieldNames
            allFieldNames |= fieldNames 
            match = classmatch.match(repr(parent))
            self._fullClassnames.append(match.group(1))
            className = match.group(2)
            for field in fieldNames :
                if pythonfield.match(field) :
                    continue
                self._inheritedFields[field] = parent
                self.__dict__[field]=None
                name = className + "." + field
                if type(parent.__getattribute__(field)) == \
                   type(parent.__getattribute__("hashCode")):
                        name += "()"
                self._inheritedFieldNames.append(name)
        self.__initialized = True
        
    def __repr__(self) :
        return "%s object at %s:\n\n%s" % (self.__class__, hex(id(self)), str(self))

    def __str__(self) :
        """The type of object"""
        string= "Python object derived from java classes:\n"
        for parent in self._fullClassnames:
            string += "\t%s\n" % parent
        string += "\nThe following java fields/methods are inherited:\n"
        for field in self._inheritedFieldNames:
            string += "\t%s\n" % field
        return string
    
    def __getattribute__(self, name) :
        """get the value of the first field with this name.
        This is also used for calling parent functions.  It has
        the weird semantics that a field can override a function,
        and vice versa"""
        fields = object.__getattribute__(self,"__dict__")
        if not fields.has_key("_JavaInheritor__initialized"):
            return object.__getattribute__(self, name)

        _inheritedFields = object.__getattribute__(self, "_inheritedFields")
        if _inheritedFields.has_key(name) :
            return _inheritedFields[name].__getattribute__(name)

        return object.__getattribute__(self, name)
            
    def __setattr__(self, name, value) :
        """Set the attr on this python object if it exists.
        Otherwise, check if the field exists on the java object,
        and set it as long as it is not a function."""
        if not self.__dict__.has_key("_JavaInheritor__initialized") or \
           (self.__dict__.has_key(name) and
            not name in self._inheritedFields.keys() ) :
            self.__dict__[name] = value
            return
        elif self.__dict__.has_key("_javaParents") and \
             name in self._inheritedFields.keys() :
            return self._inheritedFields[name].__setattr__(name, value)
        raise AttributeError("Object has no attribute '%s'" % name)
            

--- NEW FILE: KeyPress.py ---
#!/usr/bin/env python

# $Id: KeyPress.py,v 1.1 2005/09/23 10:20:33 kaminw Exp $

#                                                                      tab:2
#  "Copyright (c) 2000-2003 The Regents of the University  of California.  
#  All rights reserved.
#
#  Permission to use, copy, modify, and distribute this software and its
#  documentation for any purpose, without fee, and without written agreement is
#  hereby granted, provided that the above copyright notice, the following
#  two paragraphs and the author appear in all copies of this software.
#  
#  IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
#  DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
#  OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
#  CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#  
#  THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
#  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
#  ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
#  PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
# 
#  Copyright (c) 2002-2003 Intel Corporation
#  All rights reserved.
# 
#  This file is distributed under the terms in the attached INTEL-LICENSE     
#  file. If you do not find these files, copies can be found by writing to
#  Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, 
#  94704.  Attention:  Intel License Inquiry.
#

# 
#  @author Shawn Schaffert
#


import os


if os.name == "posix" :

    import sys, select, termios

    class KeyPress:


        def __init__(self):
            self.f = sys.stdin
            self.oldAttr = termios.tcgetattr( self.f )

            # we need to adjust some parameters of the term
            # (1) do not echo
            # (2) turn off canonical mode input (ie, switch to non-canonical mode input)
            # so that input is not processed in line chunks
            # (3) set the VMIN (the min num of bytes needed before returning during a read) to 1
            # (4) set the VTIME (the inter-byte timer) to 0, so we do not delay
            newAttr = self.oldAttr[:]
            newAttr[3] = newAttr[3] & ~termios.ECHO & ~termios.ICANON
            newAttr[6][termios.VMIN] = 1
            newAttr[6][termios.VTIME] = 0
            termios.tcsetattr( self.f , termios.TCSANOW , newAttr )

        def destroy(self):
            termios.tcsetattr( self.f , termios.TCSANOW , self.oldAttr )

        def getChar( self , blocking = False ):
            if blocking :
                ready = select.select( [self.f] , [] , [] )
            else :
                ready = select.select( [self.f] , [] , [] , 0 )

            if ready[0] :
                ch = self.f.read(1)
            else:
                ch = ""
            return ch


        
elif os.name == "nt" :

    import msvcrt

    class KeyPress:

        def __init__( self ):
            pass

        def destroy( self ):
            pass

        def getChar( self , blocking = False ):
            if blocking :
                return msvcrt.getch()
            else :
                if msvcrt.kbhit() :
                    return msvcrt.getch()
                else:
                    return ""


else :
    raise OSError




if __name__=="__main__":
    import time

    kp = KeyPress()
    i = 0
    print "press \'q\' to quit"
    while kp.getChar() != "q" :
        i += 1
        print i
        time.sleep(.25)
    kp.destroy() 

--- NEW FILE: NescApp.py ---
# "Copyright (c) 2000-2003 The Regents of the University of California.  
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose, without fee, and without written agreement
# is hereby granted, provided that the above copyright notice, the following
# two paragraphs and the author appear in all copies of this software.
# 
# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
# OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
#
# @author Kamin Whitehouse 
#

from nescDecls import *
import pytos.tools.Rpc as Rpc
import pytos.tools.RamSymbols as RamSymbols
import re

class NescTypes( object ) :
  """A class that holds all types defined in a specific nesc application.

  usage:
  myTypes = NescTypes('/path/to/nescDecls.xml')
  print myTypes
  var = myTypes.typeName
  """
  def __init__( self, xmlFilename=None, applicationName="Unknown App" ) :
    self.applicationName = applicationName
    self._typeNames = []
    self._types = {}

    #figure out the sizes of all the basic types for this platform (by scanning the xml file)
    platformTypes = {}
    typeRE = re.compile('cname=\"([\w\s]+?)\" size=\"I:(\d+?)\"')
    xmlFilename = findBuildFile(xmlFilename, "nescDecls.xml")
    infile = open(xmlFilename, 'r')
    for line in infile :
      match = typeRE.search(line)
      if match != None:
        platformTypes[match.groups()[0]] = int(match.groups()[1])      
    #define all the basic types
    self.addType(
      nescType("uint8_t", "unsigned char", "int", "type-int", "B",1,0))
    self.addType(
      nescType("int8_t", "signed char", "int", "type-int", "b", 1, 0))
    if (platformTypes.has_key("int") and platformTypes["int"] == 4) or \
       (platformTypes.has_key("unsigned int") and platformTypes["unsigned int"] == 4) :
      self.addType(
        nescType("uint16_t", "unsigned short", "int", "type-int", "H", 2, 0))
      self.addType(
        nescType("int16_t", "short", "int", "type-int", "h", 2, 0))
      self.addType(
        nescType("uint32_t", "unsigned int", "int", "type-int", "L",4,0))
      self.addType(
        nescType("int32_t", "int", "int", "type-int", "L", 4, 0))
      self.addType(
        nescType("unsigned long", "unsigned long", "int", "type-int", "L",4,0))
      self.addType(
        nescType("long", "long", "int", "type-int", "l", 4, 0))
    else : #int is 2 bytes long (the default)
      self.addType(
        nescType("unsigned short", "unsigned short", "int", "type-int", "H", 2, 0))
      self.addType(
        nescType("short", "short", "int", "type-int", "h", 2, 0))
      self.addType(
        nescType("uint16_t", "unsigned int", "int", "type-int", "H", 2, 0))
      self.addType(
        nescType("int16_t", "int", "int", "type-int", "h", 2, 0))
      self.addType(
        nescType("uint32_t", "unsigned long", "int", "type-int", "L",4,0))
      self.addType(
        nescType("int32_t", "long", "int", "type-int", "l", 4, 0))
    self.addType(
      nescType("int64_t", "long long", "long", "type-int", "q", 8, 0))
    self.addType(
      nescType("uint64_t", "unsigned long long", "long", "type-int", "Q", 8, 0))
    self.addType(
      nescType("float", "float", "float", "type-float", "f", 4, 0))
    if platformTypes.has_key("double") and platformTypes["double"] == 8 :
      self.addType(
        nescType("double", "double", "float", "type-float", "d", 8, 0))
    else : #double is 4 bytes (the default)
      self.addType(
        nescType("double", "double", "float", "type-float", "f", 4, 0))
    self.addType(
      nescType("char", "char", "str", "type-int", "c", 1, '\x00'))
    self.addType(
      nescType("void", "void", "", "type-void", "", 0, ''))

    #some arrays for error reporting:
    self.unknownStructs = []
    self.anonymousStructs = []
    self.anonymousRefStructs = []
    self.undefinedTypes = []
    self.createTypesFromXml(xmlFilename)
    self._typeNames.sort()
    #self.printSkippedTypes()
  
  def addType(self, value) :
    if not value.nescType in self._typeNames :
      self._typeNames.append(value.nescType)
    self._types[value.nescType] = value #XXX: why does this have to be unconditional??
    if not self._types.has_key(value.cType):
      self._types[value.cType] = value
      self._typeNames.append(value.cType)
    
  def __getattr__(self, name) :
    if name in self._typeNames :
      return deepcopy(self._types[name])
    else:
      raise AttributeError("No type \"%s\" defined" % name)
  
  def __getitem__(self, key) :
    if key in self._typeNames :
      return deepcopy(self._types[key])
    else:
      raise AttributeError("No type \"%s\" defined" % key)

  def __repr__(self) :
    return "%s object at %s:\n\n\t%s" % (self.__class__, hex(id(self)), str(self))
    
  def __str__(self) :
    """ Print all available types."""
    string = "\n"
    for t in self._typeNames :
      string += "\t%s\n" % t
    return string
    
  def createTypesFromXml(self, xmlFilename) :
    """Go through the struct and typedef elements in the nescDecls.xml file"""
    
    dom = minidom.parse(xmlFilename)
    typeDefs = [node for node in dom.getElementsByTagName("struct")]
    for node in dom.getElementsByTagName("typedef") :
      typeDefs.append(node)
    
    numSkipped = 0

    #keep going through the queue until it is empty
    while len(typeDefs) > 0:
      typeDef = typeDefs.pop(0)

      #if this is a typedef, see if the value is there
      if typeDef.tagName == "typedef" :
        value = typeDef.getAttribute("value")
        name = typeDef.getAttribute("name")
        #if the real value exists and typedef doesn't already exist, copy and rename original
        if self._types.has_key(value) :
          newType = deepcopy(self._types[value])
          newType.nescType = name
          self.addType(newType)
          numSkipped=0
        else :
          #try again later
          typeDefs.append(typeDef)
          numSkipped += 1
          
      else :
        #if all types within the struct are already defined, it can be defined
        try :
          self.addType(nescStruct(self, typeDef ) )
          numSkipped=0

        except Exception, e:
          if len(e.args) > 0 and e.args[0] == "Undefined struct":
            #otherwise, put it back in the queue and move on to the next one
            typeDefs.append(typeDef)
            numSkipped += 1
          elif len(e.args) > 0 and e.args[0] == "Anonymous struct" :
            self.anonymousStructs.append(typeDef)
          elif len(e.args) > 0 and e.args[0] == "Anonymous struct reference" :
            self.anonymousRefStructs.append( (typeDef, e.args[1]) )
          elif len(e.args) > 0 and e.args[0] == "Unknown type" :
            self.unknownStructs.append( (typeDef, e.args[1]) )
          else :
            #if it's an unknown exception, reraise it
            raise
      
      #make sure we are not cycling endlessly
      if numSkipped >= len(typeDefs) > 0:
        self.undefinedTypes = typeDefs
        break

  def printSkippedTypes(self):
    err = ""
    if len(self.anonymousStructs) >0 :
      err += "\nWarning: %d structs were anonymous." % len(self.anonymousStructs)
#        for struc in anonymousStructs :
#            err += "\t%s\n" % struc.getAttribute("ref")
    if len(self.anonymousRefStructs) >0 :
      err += "\nWarning: The following structs referenced anonymous structs:\n"
      for pair in self.anonymousRefStructs :
        err += "\t%s\n" % pair[0].getAttribute("name")
    if len(self.undefinedTypes) >0 :
      err += "\nWarning: The following types are ill-defined or had circular dependencies:\n"
      for struc in self.undefinedTypes :
        err += "\t%s\n" % struc.getAttribute("name")
    if len(self.unknownStructs) >0 :
      err += "\nWarning: The following structs had unknown xml types:\n"
      for pair in self.unknownStructs :
        err += "\t%s (%s)\n" % (pair[0].getAttribute("name"),
                                pair[1].tagName )
    if len(err) > 0 : print err
    
  def getTypeFromXML(self, xmlDefinition) :
    """Find the type name value given an xml definition.
    If it is an array or pointer, define the new type here."""

    #first, see if the tag is type or if child is type
    if xmlDefinition.tagName.find("type-") < 0 or \
           xmlDefinition.tagName.find("type-qualified") >= 0 :
      foundType = 0
      childNodes = [node for node in xmlDefinition.childNodes
                    if node.nodeType == 1]
      for tag in childNodes :
        if tag.tagName.find("type-") >= 0 :
          foundType += 1
          typeTag = tag
      if foundType < 1 :
        raise Exception("No type tag found")
      if foundType > 1 :
        raise Exception("Too many type tags found")
      else :
        return self.getTypeFromXML(typeTag)

    #now check all the existing types to see if it is one of them
    for val in self._typeNames :
      typeObj = self._types[val]
      if typeObj.isType(xmlDefinition) :
        return deepcopy(typeObj)

    #if the type doesn't already exist, try creating a new one
    try :
      return nescArray(self, xmlDefinition)
    except Exception, e:
        if len(e.args) <= 0 or e.args[0] != "Not array definition":
          raise
    try :
      return nescPointer(self, xmlDefinition)
    except Exception, e:
        if len(e.args) <= 0 or e.args[0] != "Not pointer definition":
          raise
      
    #it is not a simple type, array, or pointer,
    #so it must be a yet undefined struct
    child = getUniqueChild(xmlDefinition)
    if ( xmlDefinition.tagName == "type-tag" and child != None and
         child.tagName == "struct-ref" ):
         if child.hasAttribute("name"):
             raise Exception("Undefined struct")
         else :
             raise Exception("Anonymous struct reference", child)
    else:
      #otherwise, raise an exception
      #(but first make sure the right kind of unknown type is displayed)
      if  xmlDefinition.tagName == "type-tag":
          xmlDefinition = child
      raise Exception("Unknown type", xmlDefinition)






class NescEnums( object ) :
  """A class that holds all enums defined in a specific nesc application.

  usage:
  myEnums = NescEnums('/path/to/nescDecls.xml')
  print myEnums
  var = myEnums.enumName
  """

  def __init__( self, xmlFilename=None, applicationName="Unknown App" ) :
    self.applicationName = applicationName
    self._enums = []
    if type(xmlFilename) == str:
      xmlFilename = findBuildFile(xmlFilename, "nescDecls.xml")
      xmlFilename = minidom.parse(xmlFilename)
    self.createEnumsFromXml(xmlFilename)

  def __getitem__(self, key) :
    if key in self._enums :
      return self.__dict__[key]
    else:
      raise AttributeError("No such enum defined")
      
  def createEnumsFromXml(self, dom) :

    #now define all the struct types
    enumDefs = [node for node in dom.getElementsByTagName("enum")]
    integer = re.compile('^I:(\d+)$')
    hexidecimal = re.compile('^(0x[\dabcdefABCDEF]+)$')
    
    for enumDef in enumDefs :
      name = enumDef.getAttribute("name")
      if name in self._enums :
        continue
      value = enumDef.getAttribute("value")
      match = integer.match(value)
      if match != None :
        self.__dict__[name] = int(match.groups()[0])
      else :
        match = hexidecimal.match(value)
        if match != None :
          self.__dict__[name] = int(match.groups()[0], 16)
        else :
          self.__dict__[name] = value
      self._enums.append(name)
      
    namedEnums = [node for node in dom.getElementsByTagName("namedEnum")]
    for namedEnum in namedEnums :
      name = namedEnum.getAttribute("name")
      self.__dict__[name] = NescEnums(namedEnum,name)
      self._enums.append(name)
    
  def __repr__(self) :
    return "%s object at %s:\n\n\t%s" % (self.__class__, hex(id(self)), str(self))
  
  def __str__(self) :
    """ Print all available enums."""
    string = "\n"
    for key in self._enums :
      string += "\t%s = %s\n" % (key, str(self[key]))
    return string
    



class NescMsgs( object ) :
  """A class that holds all msgs defined in a specific nesc application.
  It assumes a struct is a message if AM_STRUCTNAME is defined.

  usage:
  myMsgs = NescMsgs(myTypes, myEnums[, applicationName])
  print myMsgs
  var = myMsgs.msgName
  """
  def __init__( self, types, enums, applicationName="Unknown App" ) :
    self.applicationName = applicationName
    msgTypes = [enum for enum in enums._enums if enum.find("AM_") ==0]
    name = re.compile("^AM_(\w+)$")
    self._msgNames = []
    self._msgs = {}
    for msgType in msgTypes :
      if type(enums[msgType]) == int:
        msgName = name.match(msgType)
        if msgName != None :
          msgName = msgName.groups()[0]
        for key in types._typeNames :
          if key.lower() == msgName.lower() :
            msg = TosMsg(enums[msgType], types[key])
            self._msgs[key] = msg
            self._msgNames.append(key)
            break

  def __getattr__(self, name) :
    if name in self._msgNames :
      return deepcopy(self._msgs[name])
    else:
      raise AttributeError("No such message defined")
  
  def __getitem__(self, key) :
    if key in self._msgNames :
      return deepcopy(self._msgs[key])
    else:
      raise AttributeError("No such message defined")
      
  def __repr__(self) :
    return "%s object at %s:\n\n\t%s" % (self.__class__, hex(id(self)), str(self))
  
  def __str__(self) :
    """ Print all available msgs."""
    string = "\n"
    for key in self._msgNames :
      string += "\t%5d : %s\n" % (self._msgs[key].amType, key)
    return string
    

class Shortcut (object) :
  """A class that provides all rpc functions and ram symbols for a module"""

  def __init__(self, moduleName, rpc, ram):
    self.moduleName = moduleName
    self.rpc = rpc
    self.ram = ram

  def __getattr__(self, name) :
    try :
      return self.ram.__getattr__(name)
    except :
      try:
        return self.rpc.__getattr__(name)
      except :
        raise Exception("Module %s does not have rpc function or ram symbol %s" % (
          self.moduleName, name))

  def __repr__(self):
    return "%s object at %s:\n\n%s" % (self.__class__, hex(id(self)), str(self))
  
  def __str__(self):
    string = "Module %s\n\n" % self.moduleName
    if self.ram:
      string += "%s\n" % str(self.ram)
    if self.rpc:
      string += "%s\n" % str(self.rpc)
    return string




class NescApp( object ) :
  """A class that holds all types and enums defined in a specific nesc application.

  usage:
  myApp = nescApp('/path/to/nescDecls.xml')
  print myApp
  var = myApp.enums.enumName
  var = myApp.types.typeName
  """
  def __init__( self, buildDir=None, port=None, tosbase=True, applicationName="Unknown App" ) :
    #first, import all enums, types, msgs, rpc functions, and ram symbols
    self.applicationName = applicationName
    try:
      f = findBuildFile(buildDir, "nescDecls.xml")
    except Exception, e:
      if len(e.args) > 0 and e.args[0].find("File ") == 0:
        raise Exception("%s.  Perhaps you provided the wrong build directory, or perhaps you did not compile with the \"nescDecls\" build argument.  Your nesC app cannot be imported." % e.args[0])
      else :
        raise
    self.enums = NescEnums(buildDir, applicationName)
    self.types = NescTypes(buildDir, applicationName)
    self.msgs = NescMsgs(self.types, self.enums, applicationName)
    try:
      f = findBuildFile(buildDir, "rpcSchema.xml")
    except Exception, e:
      if len(e.args) > 0 and e.args[0].find("File ") == 0:
        print "Warning: %s.  You did not compile with the \"rpc\" make argument.  No rpc commands or ram symbols will be imported." % e.args[0]
        return
      else :
        raise

    self.rpc = Rpc.Rpc(self, buildDir, sendComm=port, tosbase=tosbase)
    self.ramSymbols = RamSymbols.RamSymbols(self, buildDir,
                                            sendComm=self.rpc.sendComm,
                                            receiveComm=self.rpc.receiveComm,
                                            tosbase=tosbase)
    #now create shortcuts to all the application modules
    moduleNames = {}
    self._moduleNames = []
    moduleName = re.compile('^(\w+).')
    names = self.rpc._messages.keys()
    names.extend(self.ramSymbols._messages.keys())
    names.sort()
    for name in names :
      match = moduleName.match(name)
      if match != None :
        moduleNames[match.groups(0)[0]] = True
    for name in moduleNames.keys() :
      try :
        rpc = self.rpc.__getattr__(name)
      except:
        rpc = None
      try :
        ram = self.ramSymbols.__getattr__(name)
      except:
        ram = None
      self.__dict__[name] = Shortcut(name, rpc, ram)
      self._moduleNames.append(name)
    self._moduleNames.sort()
        
  def __repr__(self) :
    return "%s object at %s:\n\n%s" % (self.__class__, hex(id(self)), str(self))
  
  def __str__(self) :
    """ Print all application declarations."""
    string = "%20s : %d\n" % ("Enums", len(self.enums._enums))
    string += "%20s : %d\n" % ("Types", len(self.types._types))
    string += "%20s : %d\n" % ("Messages", len(self.msgs._msgNames))
    if self.__dict__.has_key("rpc") :
      string += "%20s : %d\n" % ("Rpc functions", len(self.rpc._messages.keys()))
      string += "%20s : %d\n" % ("Ram symbols", len(self.ramSymbols._messages.keys()))
      string += "%20s : " % ("Modules")
      for name in self._moduleNames :
        string += "%s\n%23s" % (name,"")
    return string

--- NEW FILE: RoutingMessages.py ---
# "Copyright (c) 2000-2003 The Regents of the University of California.  
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose, without fee, and without written agreement
# is hereby granted, provided that the above copyright notice, the following
# two paragraphs and the author appear in all copies of this software.
# 
# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
# OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
#
# @author Kamin Whitehouse 
#

"""\
RoutingMessages:  This is a package of classes that have some functionality
commonly used by routing messages, eg. Rpc and RamSymbol.
This class is not intended to be used on its own.
"""

import sys, string, time, types
import pytos.util.nescDecls as nescDecls
import pytos.Comm as Comm
import pytos.tools.Drip as Drip
import pytos.tools.Drain as Drain
from copy import deepcopy

class RoutingMessage( nescDecls.TosMsg ) :

  def __init__(self, parent, amType, *structArgs) :
    #store the parent
    self.parent = parent
    #initialize the default call parameters to none (ie, use the parent's defaults)
    for (callParam,default) in self.parent.defaultCallParams :
      self.__dict__[callParam] = None
    nescDecls.TosMsg.__init__(self, parent.app.enums.AM_RPCCOMMANDMSG, *structArgs)

  def _assignParam(self, field, param, paramId) :
    """assign a call parameter to the correct field (checking types)"""
    if type(field) == nescDecls.nescType and (
       type(param) == int or type(param) == long or
       type(param) == float or type(param) == str or
       type(param) == unicode ) :
      field.value = param
    elif type(field) == type(param) :
      field = param
    else :
      raise Exception("Illegal parameter type for param #%s.  Requires type %s." % (
        str(paramId), str(type(field))) )
    
  def _send(self, address, *posArgs, **nameArgs) :
    commArgs = ()
    
    #posArgs and nameArgs now contain only field values.
    #now assign them to the appropriate RoutingMessage fields.
    #create a temporary RoutingMessage to hold the call-time parameters
    thisCall = deepcopy(self)
    for i in range(len(posArgs)) :
      thisCall._assignParam(thisCall.value[thisCall.fields[i+1]["name"]], posArgs[i], i)
    for key in nameArgs.keys() :
      if not thisCall.value.has_key(key) :
        raise Exception("parameter name %s non-existent" % key)
      thisCall._assignParam(thisCall.value[key], nameArgs[key], key)

    thisCall.parent.sendComm.send(address, thisCall, *commArgs)
      
  def parseCallParams(self, nameArgs) :
    callParams = self.getCallParams()
    #parse any call-time call parameters
    for param in nameArgs.keys() :
      if callParams.has_key(param) :
        callParams[param] = nameArgs[param]
        del nameArgs[param]
    return callParams


  def getCallParams(self) :
    """Use the default call parameters from the parent module, but if I have the same
    field with a non-None value, use it instead"""

    callParams = self.parent.getCallParams()
    for param in callParams.keys() :
      if self.__dict__.has_key(param) and self.__getattribute__(param) != None :
        callParams[param] = self.__getattribute__(param)
    return callParams

  def __repr__(self) :
    """full function name"""
    return "%s object at %s:\n\n%s" % (self.__class__, hex(id(self)), str(self))
    
  def register(self, listener, comm=()) :
    self.parent.receiveComm.register(self, listener, *comm)
    
  def unregister(self, listener, comm=()) :
    self.parent.receiveComm.unregister(self, listener, *comm)


class Shortcut (object):
  """used to allow multiple levels of indirection w/routing messages using dots;
  ie., to allow something.module.interface.RoutingMessage()"""

  def __init__(self, parent, name):
    self.parent = parent
    self.name = name

  def __getattr__(self, name) :
    name = self.name + "." + name
    if self.parent._messages.has_key(name) :
      return self.parent._messages.get(name)
    else :
      for message in self.parent._messages.values() :
        if message.nescType.find(name+".") == 0 :
          return Shortcut(self.parent,name)
      raise Exception("Cannot find %s. Check spelling." % name)

  def __repr__(self):
    return "%s object at %s:\n\n%s" % (self.__class__, hex(id(self)), str(self))
  
  def __str__(self):
    string = ""
    funcs = ()
    messageNames = self.parent._messages.keys()
    messageNames.sort()
    for message in messageNames :
      if message.find(self.name) == 0 :
        string += str(self.parent._messages[message])
    string = string.replace(self.name + "." , "" )
    return string


  
class RoutingMessages(object) :
  
  def __init__(self, app, sendComm=None, receiveComm=None, sendChannel=None, tosbase=True) :
    self.app = app
    self.sendComm = sendComm
    if sendComm == None :
      raise Exception("You must pass something in sendComm")
    elif type(sendComm) == str :
      if sendChannel == None:
        self.sendComm = Comm.Comm(app)
        self.sendComm.connect(sendComm)
      else :
        self.sendComm = Drip.Drip(app, sendChannel, sendComm)
      self.address=app.enums.TOS_BCAST_ADDR
    self.receiveComm = receiveComm
    if receiveComm == None :
      receiveComm = sendComm
      self.receiveComm = receiveComm
    if type(receiveComm) == str :
      self.returnAddress = 0xfffe
      self.receiveComm = Drain.Drain(app, self.returnAddress, receiveComm)
      self.receiveComm.usingTosBase=tosbase 
      self.receiveComm.delay=60            #rebuild the tree every 5 mins
      self.receiveComm.forever=True
      self.receiveComm.VERBOSE=False
      self.receiveComm.maintainTree()

    self._messages = {}

  def initializeCallParams(self, callParams) :
    for (callParam,defaultVal) in self.defaultCallParams :
      if callParams.has_key(callParam) :
        self.__dict__[callParam] = callParams[callParam]
      elif not self.__dict__.has_key(callParam):
        self.__dict__[callParam] = defaultVal

  def getCallParams(self) :
    callParams = {}
    for (callParam,default) in self.defaultCallParams :
      callParams[callParam] = self.__dict__[callParam]
    return callParams

  def __getattr__(self, name) :
    for function in self._messages.values() :
      if function.nescType.find(name + ".") == 0 :
        return Shortcut(self,name)
    raise AttributeError("No such attribute %s" % name)
    
  def __repr__(self) :
    return "%s object at %s:\n\n%s" % (self.__class__, hex(id(self)), str(self))
  
  def __str__(self) :
    """ Print all available RoutingMessages."""
    string = ""
    keys = self._messages.keys()
    keys.sort()
    for name in keys :
      string += str( self._messages[name])
    return string


--- NEW FILE: SupplementQuery.py ---
#!/usr/bin/python -i
#
# "Copyright (c) 2000-2003 The Regents of the University of California.  
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose, without fee, and without written agreement
# is hereby granted, provided that the above copyright notice, the following
# two paragraphs and the author appear in all copies of this software.
# 
# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
# OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
#
# @author August Joki
#
# *This script pulls the tos_image.xml file of the currently running
# program off of the mote attached to the computer.
# *(Only if the program has been loaded into the flash)
# *Parses the xml file to get the supplement.tar.bz2 file
# *Extracts the supplement files to the directory provided
# *Sets up the pytos environment based on the files in the supplement
#
# TODO: work through tosbase(?)
#
# Usage:
# SupQuery.py [dir] [motecom]
#
# Where "dir" is the directory where the supplement files will be downloaded to.
#  Creates directory if it doesn't exist.
#  Uses the current directory if nothing is supplied.
#
# Where "motecom" is the standard comm port definition, eg "sf at localhost:9001"
#  Uses MOTECOM if nothing is supplied
#
# Deluge parsing taken from read_deluge_supplement
#  Thanks Gil

import sys, os, re
from xml.dom.minidom import parse
from binascii import unhexlify

# Check command line args
supDir = None
comm = os.getenv("MOTECOM")
if len(sys.argv) > 2:
    supDir = sys.argv[1]
    comm = sys.argv[2]
elif len(sys.argv) >1:
    if sys.argv[1].find("@") != -1:
        if sys.argv[1].find(":") != -1:
            comm = sys.argv[1]
    else:
        supDir = sys.argv[1]

# Parse deluge ping response
print "Determining Running Application...."
deluge = os.popen("MOTECOM="+comm+" java net.tinyos.tools.Deluge -p",'r')
pinging = re.compile("^Pinging node ...")
connected = re.compile("^Connected to Deluge node.")
progname = re.compile("Prog Name:\\s+(.*)")
compon = re.compile("Compiled On:\\s+(.*)")
userhash = re.compile("User Hash:\\s+(.*)")
stored = re.compile("Stored Image (\\d)")
platform = re.compile("Platform:\\s+(.*)")
userid = re.compile("User ID:\\s+(.*)")
hostname = re.compile("Hostname:\\s+(.*)")
numpages = re.compile("Num Pages:\\s+(.*)")
currentProgram = {}
storedPrograms = []
deluged = -1
line = deluge.readline().rstrip()
if pinging.search(line) != None:
    print line
    line = deluge.readline().rstrip()
    if connected.search(line) != None:
        print line
        dashes = deluge.readline().rstrip() #---...
        print dashes
        print deluge.readline().rstrip() #Curr...
        st = deluge.readline().rstrip() #Prog...
        print st
        currentProgram["progName"] = progname.search(st).group(1)
        st = deluge.readline().rstrip() #Comp...
        print st
        currentProgram["compOn"] = compon.search(st).group(1)
        st = deluge.readline().rstrip() #User...
        print st
        currentProgram["userHash"] = userhash.search(st).group(1)
        print dashes
        
        # Collect image infos
        eof = False
        while(not eof):
            st = deluge.readline().rstrip()
            if stored.search(st) != None:
                num = int(stored.search(st).group(1))
                prog = {}
                prog["progName"] = progname.search(deluge.readline().rstrip()).group(1)
                prog["compOn"] = compon.search(deluge.readline().rstrip()).group(1)
                prog["platform"] = platform.search(deluge.readline().rstrip()).group(1)
                prog["userId"] = userid.search(deluge.readline().rstrip()).group(1)
                prog["hostname"] = hostname.search(deluge.readline().rstrip()).group(1)
                prog["userHash"] = userhash.search(deluge.readline().rstrip()).group(1)
                prog["numPages"] = numpages.search(deluge.readline().rstrip()).group(1)
                storedPrograms.insert(num, prog)
                if (currentProgram["progName"] == prog["progName"] and
                    currentProgram["compOn"] == prog["compOn"]     and
                    currentProgram["userHash"] == prog["userHash"]):
                    deluged = num

            else:
                eof = True

        # Running program not in flash so no supplement available
        if deluged == -1:
            index = 0
            print "Currently running program has not been Deluged.\nNo supplement to download."
            print dashes
            print "  Stored Programs:"
            for prog in storedPrograms:
                if prog["progName"] != "N/A":
                    name = prog["progName"]
                    comp = prog["compOn"]
                    uhash = prog["userHash"]
                    print "    "+str(index)
                    print "      Prog Name: "+name
                    print "      Compiled On: "+comp
                    print "      User Hash: "+uhash
                index += 1
            sys.exit()


deluge.close()

# Time to download tos_image.xml off mote
if not os.access(supDir,os.F_OK):
    os.makedirs(supDir)
os.chdir(supDir)

st = ''
if supDir != None:
    st = supDir+"/tos_image.xml"

print "Downloading image to "+st+"."
print "This could take a while...."
os.popen("MOTECOM="+comm+" java net.tinyos.tools.Deluge -d -in="+str(deluged)+" -o=tos_image.xml",'r')
print "Done downloading image."

# Extract supplement
print "Extracting supplement."
dom = parse("tos_image.xml")
supxml = dom.getElementsByTagName("supplement")[0]
format = supxml.getAttribute("format")
sup = supxml.firstChild.data
dom.unlink()

# Parse supplement archive
print "Parsing supplement.tar.bz2."
whitespace = re.compile("\\s+")
sup = whitespace.sub('',sup)
if format == "hex":
    #if supDir != None:
    #    os.chdir(supDir)
    supfile = open("sup.hx",'w')
    supfile.write(str(sup))
    supfile.close()
    supfile = open('sup.pl','w')
    supfile.write('open(HX, "sup.hx"); my $hex = <HX>; my $bin = pack("h*", $hex); open(SUP, ">supplement.tar.bz2"); print SUP $bin;')
    supfile.close()
    t = os.system('perl sup.pl')
    print t

# Unpack supplement file
print "Untaring suplement.tar.bz2"
os.system("tar xjf supplement.tar.bz2")
os.remove("sup.hx")
os.remove("sup.pl")
os.remove("tos_image.xml")

# Set up pytos environment like normal
print "Now setting up pytos environment...."

import pytos.tools.Rpc as Rpc
import pytos.tools.RamSymbols as RamSymbols
import pytos.util.NescApp as NescApp

app = NescApp.NescApp(supDir, comm, tosbase=False)

--- NEW FILE: Timer.py ---
#$Id: Timer.py,v 1.1 2005/09/23 10:20:33 kaminw Exp $

# "Copyright (c) 2000-2003 The Regents of the University of California.  
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose, without fee, and without written agreement
# is hereby granted, provided that the above copyright notice, the following
# two paragraphs and the author appear in all copies of this software.
# 
# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
# OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."

# @author Shawn Schaffert <sms at eecs.berkeley.edu>

import threading

class Timer( object ) :

  def __init__( self , callback=None , period=0 , numFiring=0 , waitTime=0 ) :
    self.period = period   #must be >= 0
    self.waitTime = waitTime  #must be >=0
    self.numFiring = numFiring  # 0 = forever, 1 = one-shot , 2+ = finite repeats
    self.callback = callback

  def __fireNext( self ) :
    if self.numFiring == 0 :
      self.timer = threading.Timer( self.period , self.__callback ).start()
    elif self.remainingFirings == 0 :
      self.timer = None
    else :
      self.timer = threading.Timer( self.period , self.__callback ).start()
      self.remainingFirings -= 1

  def __callback( self ) :
    if self.stopTimer :
      self.timer = None
    else :
      self.__fireNext()
      if self.callback:
        self.callback()

  def __waitOver( self ) :
    self.__fireNext()

  def start( self ) :
    self.timer = None
    self.remainingFirings = self.numFiring
    self.stopTimer = False
    if self.waitTime > 0 :
      self.timer = threading.Timer( self.waitTime , self.__waitOver ).start()
    else :
      self.__fireNext()

  def cancel( self ) :
    self.stopTimer = True

--- NEW FILE: __init__.py ---
# "Copyright (c) 2000-2003 The Regents of the University of California.  
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose, without fee, and without written agreement
# is hereby granted, provided that the above copyright notice, the following
# two paragraphs and the author appear in all copies of this software.
# 
# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
# OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
#
# @author Kamin Whitehouse 
#


--- NEW FILE: nescDecls.py ---
# "Copyright (c) 2000-2003 The Regents of the University of California.  
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose, without fee, and without written agreement
# is hereby granted, provided that the above copyright notice, the following
# two paragraphs and the author appear in all copies of this software.
# 
# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
# OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
#
# @author Kamin Whitehouse 
#

import sys, string, math, re, os
from struct import *
from xml.dom import minidom
from jpype import jimport
from copy import deepcopy

###########
# This class can be used to hold a basic nesc type, eg uint8_t It can
# be set and get through nescType.value, and does type checking
# 
###########

def findBuildFile(givenString, desiredFilename) :
  """This function will find a desiredFilename (eg. nescDecls.xml) the build directory
  from a givenString (e.g 'build/pc').  Legal givenStrings are:
  1.  Full path,     eg: /home/kamin/tinyos-1.x/...
  2.  relative path, eg: apps/TestRpc/build/pc
  3.  platform name, eg: pc or telosb
  """

  #check to see if the given string contains the desiredFilename
  if givenString.find(desiredFilename) >= 0 :
    filename = givenString

  #then check to see if it is an absolute or relative path
  elif givenString.find('/') >= 0 :
    filename = givenString + desiredFilename

  #then check to see if it is just the platform name
  elif len(givenString) > 0:
    filename = 'build/' + givenString + '/' + desiredFilename

  #check if a default platform environment variable is defined
  elif os.environ.has_key("TINYOS_DEFAULT_PLATFORM") :
    filename = 'build/' + os.environ["TINYOS_DEFAULT_PLATFORM"] + '/' + desiredFilename

  #otherwise, assume the file is in './'
  else :
    filename = desiredFilename
    
  #check to see if the file was successfully found
  if not os.path.isfile(filename) :
    raise IOError("File %s not found" % filename)
  return filename

  
class nescType( object ) :
  """A python representation of a nesc type.

  usage:
  X = nescType.value
  nescType.value = X
  bytes = nescType.getBytes()
  nescType.setBytes(bytes)
  nescType
  print nescType
  """

  def __init__( self , nescType, cType, pythonType, xmlTag,
                conversionString, size, defaultValue) :
    """create a new nescType"""
    self.nescType = nescType
    self.cType = cType
    self.pythonType = pythonType
    self._xmlTag = xmlTag
    self.size = size
    self._conversionString = conversionString
    self.value = defaultValue

  def __repr__(self) :
    return "%s object at %s:\n\n%20s : %s" % (self.__class__, hex(id(self)), "value", str(self))

  def __str__(self) :
    if self._conversionString == "c" :
        return "'" + str(self.value) + "'"
    else :
        return str(self.value)

  #   this func could be used for type checking 
  def __setattr__(self, name, value) :
    if self.__dict__.has_key("value") and name == "value":
        #use the type conversions built into pack
        pack(self._conversionString, value)
    self.__dict__[name] = value

  def oneLineStr(self) :
    return str(self)

  def __deepcopy__(self, memo={}) :
    result = nescType(self.nescType, self.cType, self.pythonType,
                    self._xmlTag, self._conversionString, self.size,
                    deepcopy(self.value, memo))
    memo[id(self)] = result
    return result
  
  def isType(self, xmlDefinition) :
    """returns 1 if the xml definition describes this type.
    Returns 0 otherwise."""
    if xmlDefinition != None and xmlDefinition.tagName == self._xmlTag and \
           xmlDefinition.hasAttribute("cname") and \
           xmlDefinition.getAttribute("cname") == self.cType :
      return 1
    elif self.nescType == "void" and xmlDefinition.tagName == self._xmlTag :
      #void is a special xml case that doesn't have cname defined (grr)
      return 1
    else :
      return 0
  
  def getBytes(self) :
    """Hexidecimal representation of a value of this type"""
    if self.nescType == "void" :
      return ''
    try:
      bytes = pack(self._conversionString, self.value)
    except Exception, inst:
      print inst
      raise Exception("Bytes conversion error: %s %d bytes to %d" %
                      (self.nescType, len(bytes), self.size) )
    if len(bytes) != self.size:
      raise Exception("Wrong number of bytes for conversion: %s %d bytes to %d" %
                      (self.nescType, len(bytes), self.size))
    return bytes
  
  def setBytes(self, bytes):
    """A value of this type from a hexidecimal representation"""
    if self.nescType == "void" :
      return bytes
    if len(bytes) < self.size:
      raise Exception("Wrong number of bytes for conversion: %s %d bytes to %d" %
                      (self.nescType, len(bytes), self.size))
    try:
      self.value, = unpack( self._conversionString, bytes[:self.size])
    except Exception, inst:
      print inst
      raise Exception("Bytes conversion error: %s %d bytes to %d" %
                      ( self.nescType, len(bytes), self.size) )
    return bytes[self.size:]

###########
# Array of basic nesc types,
###########

class nescArray( object ) :
  """A python representation of a nesc array.

  usage:
  array = nescArray(size, nescType)
  array = nescArray(myTypes, xmlDecl)
  X = array[3]
  X = array[3:6] (returns a list or, if char[] array, a python string)
  array[3] = X
  array[3:6] [X,Y,Z] (or, if char[], \"foo\")
  bytes = array.getBytes()
  array.setBytes(bytes)
  array
  print array
  """

  def __init__( self , *varargs) :
    """initialize all elements to 0"""
    if len(varargs) == 0 :
      return
    elif len(varargs) == 2 and type(varargs[0]) == int :
      (self.len,self.elementType) = varargs[:]
      bracketStr = "[" + str(self.len) + "]"
    elif len(varargs) == 2 :
      (nescTypes, xmlDefinition) = varargs[:]
      if xmlDefinition.tagName != "type-array" :
        raise Exception("Not array definition")
      child = getUniqueChild(xmlDefinition)
      self.elementType = nescTypes.getTypeFromXML(child)
      sizeStr = xmlDefinition.getAttribute("elements")[2:]
      self.len = int(sizeStr)
      bracketStr = "[" + sizeStr + "]"
    else :
      raise Exception("Illegal array params")
    self.nescType = self.elementType.nescType + bracketStr
    self.cType = self.elementType.cType + bracketStr
    self.pythonType = self.elementType.pythonType + bracketStr
    self.size = self.len * self.elementType.size
    self.value = []
    for i in range(self.len):
      self.value.append(deepcopy(self.elementType))
      

  def __repr__(self) :
    """A printable representation of the value"""
    return "%s object at %s:\n\n\t%s" % (self.__class__, hex(id(self)), str(self))
    
  def __str__(self) :
    """A printable representation of the value"""
    string = "nescArray of type %s:\n" % self.nescType
#     if self.elementType._conversionString == "c":
#       string += self.oneLineStr()
#     else:
    for i in range(self.len) :
      string += "%2d: %s\n" % (i, self.value[i].oneLineStr())
    return string

  def __getitem__(self, key) :
      if self.elementType.__class__ == nescType :
          if key.__class__ == slice:
              if self.elementType._conversionString == "c":
                  string = ""
                  for item in self.value.__getitem__(key) :
                      string += item.value
                  return string
              else:
                  return [item.value for item in self.value.__getitem__(key)]
          else:
              return self.value.__getitem__(key).value
      else:
          return self.value.__getitem__(key)
      
  def __setitem__(self, key, value) : 
      if self.elementType.__class__ == nescType :
        if key.__class__ == slice:
            i=0;
            for item in self.value.__getitem__(key) :
                item.value = value[i]
                i += 1
        else:
            self.value.__getitem__(key).value = value
      else :
          self.value.__setitem__(key, value)

  def __delitem__(self, key) : 
      return self.value.__delitem__(key)

  def oneLineStr(self) :
    """A one-line representation of the value"""
    #maybe the string should just print like a string
    #but the \x00 chars look like nothing
#     if self.elementType._conversionString == "c":
#       string = '\''
#       for c in self.value :
#         string += c.value
#       string += '\''
#     else:
    tmpStr = str(self.elementType)
    if tmpStr.find("\n") >= 0 or len(tmpStr) > 5 :
      return self.nescType
    else :
      i = 0; string = "["
      while len(string) < 40 and i < self.len :
        string += str(self.value[i]) + ", "
        i += 1
      if i < self.len :
        string += "...]"
      else:
        string += "\b\b]"
    return string
 
  def __deepcopy__(self, memo={}) :
    result = nescArray()
    memo[id(self)] = result
    result.elementType = deepcopy(self.elementType, memo)
    result.nescType = self.nescType
    result.cType = self.cType
    result.pythonType = self.pythonType
    result.len = self.len
    result.size = self.size
    result.value = deepcopy(self.value, memo)
    return result

  def isType(self, xmlDefinition) :
    """returns 1 if the xml definition describes this type.
    Returns 0 otherwise."""
    if ( xmlDefinition != None and xmlDefinition.tagName == "type-array" and
         int(xmlDefinition.getAttribute("elements")[2:]) == self.len ) :
      child = getUniqueChild(xmlDefinition)
      return self.elementType.isType(child)
    else :
      return 0

  def getBytes(self) :
    """Hexidecimal representation of a value of this type"""
    bytes = ""
    for i in range(self.len):
      bytes += self.value[i].getBytes()
    if len(bytes) != self.size:
      raise Exception("Byte conversion error: %s %d bytes to %d" %
                      ( self.nescType, len(bytes), self.size))
    return bytes
    
  
  def setBytes(self, bytes) :
    """A value of this type from a hexidecimal representation"""
    if len(bytes) < self.size:
        raise Exception("Byte conversion error: %s %d bytes to %d" %
                        (self.nescType, len(bytes), self.size) )
    for i in range(self.len) :
      bytes = self.value[i].setBytes(bytes)
    return bytes

###########
# Pointer to basic nesc types,
###########

class nescPointer( object ) :
  """A python representation of a nesc pointer.

  usage:
  pointer = nescPointer(ptrSize, nescType)
  pointer = nescPointer(myTypes, xmlDecl)
  nescType = pointer.value
  pointer.value = nescType
  bytes = pointer.getBytes()
  pointer.setBytes(bytes)
  pointer
  print pointer
  """
  
  def __init__( self , *varargs) :
    """initialize all elements to 0"""
    if len(varargs) == 0:
      return
    elif len(varargs) == 2 and varargs[1].__dict__.has_key("tagName"):
      (nescTypes, xmlDefinition) = varargs[:]
      if xmlDefinition.tagName != "type-pointer" :
        raise Exception("Not pointer definition")
      child = getUniqueChild(xmlDefinition)
      self.value = nescTypes.getTypeFromXML(child)
      self.size = int(xmlDefinition.getAttribute("size")[2:])
    elif len(varargs) == 2 :
      self.size = varargs[0].types["unsigned int"].size
      self.value = varargs[1]
    else :
      raise Exception("Illegal nescPointer constructor arguments")
    self.nescType = self.value.nescType + "*"
    self.cType = self.value.cType + "*"
    self.pythonType = self.value.pythonType + "*"
      
  def __repr__(self) :
    return "%s object at %s:\n\n\t%s" % (self.__class__, hex(id(self)), str(self))

  def __str__(self) :
    """A text representation of the value"""
    return "ptr-> %s" % str(self.value)

  def oneLineStr(self) :
    """A one-line representation of the value"""
    return  "ptr-> %s" % self.value.oneLineStr()

  def __deepcopy__(self, memo={}) :
    result = nescPointer()
    memo[id(self)] = result
    result.value = deepcopy(self.value, memo)
    result.size = self.size
    result.nescType = self.nescType
    result.cType = self.cType
    result.pythonType = self.pythonType
    return result

  def isType(self, xmlDefinition) :
    """returns 1 if the xml definition describes this type.
    Returns 0 otherwise."""
    if xmlDefinition != None and xmlDefinition.tagName == "type-pointer" :
      child = getUniqueChild(xmlDefinition)
      return self.value.isType(child)
    else :
      return 0

  def getBytes(self) :
      bytes = pack (str(self.size)+"s",'\x00')
      if len(bytes) != self.size:
          raise Exception("Byte conversion error: %s %d bytes to %d" %
                          (self.nescType, len(bytes), self.size) )
      return bytes
  
  def setBytes(self, bytes) :
    if len(bytes) < self.size:
        raise Exception("Byte conversion error: %s %d bytes to %d" %
                        ( self.nescType, len(bytes), self.size) )
    return bytes[self.size:]


###########
# Struct of basic nesc types,
###########

class nescStruct( object ) :
  """A python representation of a nesc structure.

  usage:
  struct = nescStruct(myTypes, xmlDecl)
  struct = nescStruct(structName, (fieldName, type) (fieldName, type), ...)
  X = struct.field
  struct.field = X
  bytes = struct.getBytes()
  struct.setBytes(bytes)
  struct
  print struct
  """

  def __init__( self, *varargs) :
    """initialize all fields to 0"""
    self.__dict__["value"] = {}
    self.fields = []
    self.size = 0
    if len(varargs) == 0 :
      self.nescType = ""
    #create the struct from nescType args
    elif len(varargs) >= 1 and ( type(varargs[0]) == str or
                                 type(varargs[0]) == unicode ) :
      self.nescType = varargs[0]
      self._parseNescTypeFields(varargs[1:])
    ## parse the struct def from xml
    elif len(varargs) == 2 and type(varargs[1]) != tuple :
      (nescTypes, xmlDefinition) = varargs[:]
      if xmlDefinition.tagName != "struct" :
        raise Exception("Not struct definition")
      if xmlDefinition.hasAttribute("name") == False:
        raise Exception("Anonymous struct")
      self.nescType = xmlDefinition.getAttribute("name")
      self.size = int(xmlDefinition.getAttribute("size")[2:])
      self._parseXMLFields(nescTypes, xmlDefinition)
    else :
      raise Exception("Illegal nescStruct constructor args")
    self.cType = self.nescType
    self.pythonType = self.nescType
    self.__initialized = True
    
  def __getattr__(self, name) :
    if self.__dict__.has_key("value") :
      if self.value.has_key(name) :
        if self.value[name].__class__ == nescType :
          return self.value[name].value
        else :
          return self.value[name]
    else :
      raise AttributeError("No such field \"%s\" in the nescStruct \"%s\"" % (name, self.nescType))

  def __setattr__(self, name, value) :
    if not self.__dict__.has_key("_nescStruct__initialized") :
      self.__dict__[name] = value
      return
    if self.value.has_key(name) :
      if self.value[name].__class__ == nescType :
          self.value[name].value = value;
      else :
          self.value[name] = value;
    elif self.__dict__.has_key(name) :
      self.__dict__[name] = value
    else :
      raise AttributeError("No such field \"%s\" in the nescStruct \"%s\"" % (name, self.nescType))
        

  def __repr__(self) :
    return "%s object at %s:\n\n\t%s" % (self.__class__, hex(id(self)), str(self))
    
  def __str__(self) :
    """All fields and values as a readable string"""
    string = self.nescType + ": \n"
    for field in self.fields :
      string += "%30s  : %s\n" % (
        "%s %s" % (self.value[field["name"]].nescType, field["name"]),
        self.value[field["name"]].oneLineStr() )
    return string

  def oneLineStr(self) :
    """A one-line representation of the struct"""
    return self.nescType

  def __deepcopy__(self, memo={}) :
    result = self.__class__()
    memo[id(self)] = result
    self._copyFields(result, memo)
    return result

  def _copyFields(self, other, memo=None) :
    other.size = self.size
    other.nescType = self.nescType
    other.cType = self.cType
    other.pythonType = self.pythonType
    if memo == None :
      other.value = deepcopy(self.value)
      other.fields = deepcopy(self.fields)
    else :
      other.value = deepcopy(self.value, memo)
      other.fields = deepcopy(self.fields, memo)
    other.__initialized = True

  def _parseXMLFields(self, nescTypes, xmlDefinition) :
    """Create a list of fields & values given a struct xml declaration."""
    fields = [node for node in xmlDefinition.getElementsByTagName("field")]
    fields.sort( lambda A, B :  A - B,
            lambda field: int(field.getAttribute("bit-offset")[2:]) )
    for fieldDef in fields:
      field = {}
      field["name"] = fieldDef.getAttribute("name")
      field["bitOffset"] = int(fieldDef.getAttribute("bit-offset")[2:])
      if fieldDef.hasAttribute("bit-size"):
          field["bitSize"] = int(fieldDef.getAttribute("bit-size")[2:])
      elif fieldDef.hasAttribute("size"):
          field["bitSize"] = int(fieldDef.getAttribute("size")[2:])*8
      self.fields.append(field)
      self.value[fieldDef.getAttribute("name")] = nescTypes.getTypeFromXML(fieldDef)
    #here's a weird bug in the nesc.xml generation where the "size" attribute
    #for packed structs is actually the size of the unpacked struct.
    if xmlDefinition.hasAttribute("packed") :
      self.size = self.packedSize()
    else:
      self.size = int(xmlDefinition.getAttribute("size")[2:])
  
  def _parseNescTypeFields(self, fields) :
    """Create a list of fields & values given a tuple of
    fieldname,value sequences."""
    self.size = 0
    for fieldDef in fields:
      field = {}
      (field["name"],fType) = fieldDef
      field["bitOffset"] = self.size*8
      field["bitSize"] = fType.size*8
      self.fields.append(field)
      self.value[field["name"]] = fType
      self.size += fType.size
  
  def isType(self, xmlDefinition) :
    """returns 1 if the xml definition describes this type.
    Returns 0 otherwise."""
    if xmlDefinition == None :
      return 0
    child = getUniqueChild(xmlDefinition)
    if ( ( xmlDefinition.tagName == "struct" and
           xmlDefinition.getAttribute("name") == self.nescType) or
         ( xmlDefinition.tagName == "type-tag" and child != None and 
           child.tagName == "struct-ref" and
           child.getAttribute("name") == self.nescType ) ) :
      return 1
    else :
      return 0

  def getBytes(self) :
    """Hexidecimal representation of struct"""
    # We have to be careful in here about:
    # 1.  bit fields (ie. bitSize shorter than nominal type size)
    # 2.  packing (ie. bits that are not part of any particular field)
    bits = ""
    for field in self.fields :
      for i in range(len(bits), field["bitOffset"]) :
        bits += "0"
      newBits = hex2bin(self.value[field["name"]].getBytes())
      bits += newBits[-field["bitSize"]:]
      #the following loop is just type checking for bit fields.  Can we do this on setattr?
      for i in range(len(newBits)-field["bitSize"]):
          if newBits[i] == "1":
              print "Bit-field type error: value of %s.%s being truncated" % (self.nescType,
                                                        field["name"])
    for i in range(len(bits), self.size*8) :
      bits += "0"
    bytes = bin2hex(bits)
    if len(bytes) != self.size:
      raise Exception("Byte conversion error: %s %d bytes to %d" %
                      ( self.nescType, len(bytes), self.size))
    return bytes

  def setBytes(self, bytes) :
    """Set all values using hexidecimal representation"""
    # We have to be careful in here about:
    # 1.  bit fields (ie. bitSize shorter than nominal type size)
    # 2.  packing (ie. bits that are not part of any particular field)
    if len(bytes) < self.size:
      raise Exception("Byte conversion error: %s %d bytes to %d" %
                      (self.nescType, len(bytes), self.size) )
    bits = hex2bin(bytes)
    for field in self.fields :
      newBits = ""
      for i in range(self.value[field["name"]].size*8) :
        newBits += "0"
      selectedBits=bits[field["bitOffset"]:field["bitOffset"]+field["bitSize"]]
      newBits = newBits[:-field["bitSize"]] + selectedBits
      newBytes = ""
      for i in range(self.value[field["name"]].size) :
        newBytes += '\x00'
      tmpBytes = bin2hex(newBits)
      newBytes = newBytes[:-len(tmpBytes)] + tmpBytes
      self.value[field["name"]].setBytes(newBytes);
    return bytes[self.size:]

  def packedSize(self) :
    if len(self.fields) == 0 :
      trueSize = 0
    else :
      a,b,lastField = self._findLastNestedField()
      trueSize = (lastField["bitOffset"] + lastField["bitSize"]) /8
    return trueSize

  def _findLastNestedField(self) :
    lastField = self
    parents = []
    #find the last (possibly nested) field
    while issubclass(type(lastField), nescStruct) and len(lastField.fields) > 0 :
      parent = lastField
      lastFieldDef = parent.fields[-1]
      lastField = parent.value[lastFieldDef["name"]]
      parents.append( parent )
    return (lastField, parents, lastFieldDef)





class TosMsg ( nescStruct ) :
    """A python representation of a TosMsg.
    Is a nescStruct object.
    Can be used with
    pytos.comm.send, pytos.comm.register, pytos.comm.unregister.
    
    usage:
    msg = TosMsg(amType)
    msg = TosMsg(amType, nescStruct)
    msg = TosMsg(amType, <nescStruct constructor args>)
    print msg
    msg.field = X
    comm.send(msg)
    comm.register(msg, f)
    comm.unregister(msg, f)
    migMsg = msg.createMigMsg()
    msg.parseMigMsg(migMsg)
    """

    def __init__(self, amType, *varargs):
        self.amType = amType
        self.parentMsg = None
        #if this is a nescStruct argument, make myself a clone of it
        if len(varargs) == 1 and issubclass(type(varargs[0]), nescStruct) :
            nescStruct._copyFields(varargs[0],self)
        #otherwise, make myself into a struct with the struct args
        elif len(varargs) >= 1:
            nescStruct.__init__(self, *varargs)

    def __deepcopy__(self, memo={}) :
      result = self.__class__(self.amType)
      memo[id(self)] = result
      self._copyFields(result, memo)
      result.parentMsg = deepcopy(self.parentMsg, memo)
      return result

    def getParentMsg(self, amOrName) :
      """This function will get the parent message with the amType or name specified"""
      if self.parentMsg == None :
        return None
      elif self.parentMsg.nescType == amOrName or self.parentMsg.amType == amOrName :
        return self.parentMsg
      else :
        return self.parentMsg.getParentMsg(amOrName)
      
    def createMigMsg(self) :
        """Returns a java BaseTOSMsg with same amType and length
        and with data payload of same bytes"""
        Message = jimport.net.tinyos.message.BaseTOSMsg
        msg = Message(self.size)
        msg.dataSet(unpack( str(self.size) + 'b', self.getBytes() ) )
        msg.amTypeSet(self.amType)
#        msg.set_type( self.amType )
#        msg.set_length(self.size)
        return msg

    def parseMigMsg(self, msg) :
        """Takes a java BaseTOSMsg and creates TosMsg
        with same amType and length and with data payload of same bytes"""
        self.amType = msg.amType()
        data = list(msg.dataGet())
        self.setBytes(pack(str(len(data)) + 'b', *data))

    def __repr__(self) :
      return "%s object at %s:\n\n\t%s" % (self.__class__, hex(id(self)), str(self))
      
    def __str__(self) :
        """All fields and values as a readable string"""
        return "TosMsg(am=%d) " % self.amType + nescStruct.__str__(self)
        
    def setBytes(self, bytes) :
        """Extend this msg to be longer, if necessary to accomodate extra data.
        This only happens if the last field is a nescArray of length 0.
        Unlike nescStructs, TosMsg objects are not nested recursively, so it is
        Ok to do this."""
        if len(bytes) > self.size : #trueSize() :
            #print "there are more bytes than fit in this msg... trying to grow msg"
            lastField, parents,b = self._findLastNestedField()
            #see if it is an array of size 0
            if type(lastField) == nescArray and lastField.len == 0 :
                #make it bigger
                #print "last field is nescArray[0]... growing"
                lastFieldSize = lastField.elementType.size
                numExtraBytes = len(bytes) - self.size #trueSize()
                if numExtraBytes % lastFieldSize == 0:
                    requiredArraySize = int( numExtraBytes/lastFieldSize )
                    lastField = nescArray(requiredArraySize, lastField.elementType)
                #print "new size is %d" % numExtraBytes
                #and set it, changing the size of all parent structs
                parents.reverse()
                for parent in parents :
#                    trueSize = parent.trueSize()
                    parent.value[parent.fields[-1]["name"]] = lastField
                    parent.fields[-1]["bitSize"] = lastField.size*8
                    parent.size = self.packedSize()# + lastField.size
                    lastField = parent
            else:
                #print "last field is not nescArray[0]. Cannot grow. Ignoring extra data."
                pass
            
        #make sure everything worked out correctly and call parent's function
        if len(bytes) != self.size :#trueSize() :
            raise Exception("Incorrect number of bytes for TosMsg. Byte conversion error: %s %d bytes to %d" % ( self.nescType, len(bytes), self.size) )
        #print "passing to child to set bytes."
        nescStruct.setBytes(self,bytes)







def getUniqueChild(xmlDefinition) :
  child = None
  for childNode in xmlDefinition.childNodes :
    if childNode.nodeType == 1 :
      child = childNode
      break
  return child

def bin2hex(bits) :
    bytes = ""
    for i in range(0, len(bits), 8 ):
        bytes += pack('B',int(bits[i:i+8],2))
    return bytes

def hex2bin(bytes) :
    bits = ""
    for i in range(len(bytes)) :
        val, = unpack('B',bytes[i])
        for j in range(7,-1,-1):
            if val>= pow(2,j):
                bits += "1"
                val -= pow(2,j)
            else :
                bits += "0"
    return bits


def TestAppTypes() :
    testRpc = appTypes('/home/kamin/tinyos-1.x/contrib/hood/apps/TestRpc/build/telosb/nesc.xml')
    print testRpc
    
if __name__ == "__main__": TestAppTypes()



More information about the Tinyos-commits mailing list