[Tinyos-commits] CVS: tinyos-1.x/tools/python/pytos/tools Drain.py, NONE, 1.1 Drip.py, NONE, 1.1 RamSymbols.py, NONE, 1.1 Rpc.py, NONE, 1.1 __init__.py, NONE, 1.1

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


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

Added Files:
	Drain.py Drip.py RamSymbols.py Rpc.py __init__.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: Drain.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 jpype import jimport, JProxy, JavaException, JException           
from pytos.util.JavaInheritor import JavaInheritor
import pytos.Comm as Comm
import struct, types
import thread
import pytos.util.nescDecls as nescDecls
from copy import deepcopy

drain = jimport.net.tinyos.drain

class Drain( JavaInheritor ) :
    """The Drain object inherits from both the Drain.java and
    the DrainConnector.java objects, in that order.  It overrides the
    constructors to construct both objects from the same comm objects and
    provides register/unregister methods to handle python TosMsg objects.

    usage:
      drain = Drain(app, spAddr, 'sf at localhost:9001')
      drain = Drain(app, spAddr, moteif)  
      drain.register(myTosMsg, myMsgQueue)
      drain.unregister(myTosMsg, myMsgQueue)
      ... (plus all other functions inherited from the java objects)
    """
    
    def __init__( self , app, spAddr, moteIF ) :
        if type(moteIF) == str :
            moteIF = Comm.openMoteIF(moteIF, app)
        drainObj = drain.Drain(spAddr, moteIF)
        drainConnectorObj = drain.DrainConnector(spAddr, moteIF)
        self.app = app #save for later use
        JavaInheritor.__init__(self, (drainObj, drainConnectorObj) )

    def register( self , msg , callback, *comm ) :
        (num, callback) = self._wrapCallbackAndTosMsg(msg, callback)
        self.registerListener( num , callback )

    def unregister( self , msg , callback , *comm ) :
        (num, callback) = self._wrapCallbackAndTosMsg(msg, callback)
        self.deregisterListener( num , callback )

    def _wrapCallbackAndTosMsg(self, msg, callback) :
        callback = DrainMsgPeeler(self.app, msg, callback)
        callback = Comm.createJavaMessageListener(callback)
        num = msg.amType
        return (num, callback)
    
class DrainMsgPeeler( Comm.Mig2TosMsgConverter ) :
  """This is a wrapper callback object that peels the Drain headers out
  of a DrainMsg mig object and creates a python TosMsg with the remaining data """

  def __init__(self, app, msg, callback) :
    self.drainMsg = nescDecls.TosMsg(app.enums.AM_DRAINMSG, app.types.DrainMsg)
    Comm.Mig2TosMsgConverter.__init__(self, msg, callback )
    
  def messageReceived( self , addr , migMsg ) :
      try:
          drainMsg = deepcopy(self.drainMsg)
          drainMsg.parseMigMsg(migMsg)
          msg = deepcopy(self.msg)
          bytes = drainMsg.data.getBytes()
          msg.setBytes( bytes )
          msg.parentMsg = drainMsg
          self.callback( addr, msg ) 
      except Exception, inst:
          print inst
          raise
      

--- NEW FILE: Drip.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 jpype import jimport, JInt
from pytos.util.JavaInheritor import JavaInheritor
import pytos.Comm as Comm

drip = jimport.net.tinyos.drip

class Drip( JavaInheritor ) :
    """The Drip object inherits from the Drip.java object.  It overrides the
    constructor, and the send and sendwakeup commands to handle python TosMsg objects.

    usage:
      drip = Drip(app, Channel, 'sf at localhost:9001')
      drip = Drip(app, Channel, moteif)  
      drip.send(myTosMsg)
      drip.sendWakeup(myTosMsg)
      ... (plus all other functions inherited from the java object)

    For interface-compatbility with comm, you can also send a dest address, which is ignored:
      drip.send(addr, myTosMsg)
    """
    
    def __init__( self , app, channel, moteIF ) :
        if type(moteIF) == str :
            moteIF = Comm.openMoteIF(moteIF, app)
        dripObj = drip.Drip(channel, moteIF)
        JavaInheritor.__init__(self, (dripObj,) )

    def send( self , msg, *comm ) :
        #For interface-compatbility with comm, you can also send a dest address, which is ignored:
        if type(msg) == int and len(comm) > 0:
            msg = comm[0]
        migMsg = msg.createMigMsg()
        self.migMsgSend(migMsg, msg.size)

    def sendWakeup( self , msg, *comm ) :
        migMsg = msg.createMigMsg()
        self.migMsgSendWakeup(migMsg, msg.size)
      
    def migMsgSend( self , msg, size, *comm ) :
        self._javaParents[0].send(msg, JInt(size))

    def migMsgSendWakeup( self , msg, size, *comm ) :
        self._javaParents[0].sendWakeup(msg, size)
      

--- NEW FILE: RamSymbols.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 
#

"""\
RamSymbols.py -- a tool for poking and peeking ram symbols on motes.
To be used in conjunction with tinyos-1.x/contrib/nestfe/nesc/lib/RamSymbols

"""

import sys, string, time, types
from xml.dom import minidom
import pytos.util.nescDecls as nescDecls
import pytos.util.RoutingMessages as RoutingMessages
import pytos.Comm as Comm 
from copy import deepcopy

class RamSymbol( RoutingMessages.RoutingMessage ) :

  #this is the variable the determines, on a blocking rpc call, how
  #many messages will be queued up.  Should perhaps be bigger for large
  #networks, but will generally be the same number for all rpc
  #functions that use the same send and receive comm stacks.
  msgQueueSize = 10
  
  def __init__(self, xmlDefinition=None, parent=None) :
    if xmlDefinition==None :
      return
    self.pokeResponseMsg = None
    self.memAddress = int(xmlDefinition.getAttribute("address"))
    length = int(xmlDefinition.getAttribute("length"))
    typeDef = xmlDefinition.getElementsByTagName("type")[0]
    self.isPointer = typeDef.getAttribute("typeClass") == "pointer"
    self.isArray = xmlDefinition.hasAttribute("array")
    symbolType = parent.app.types[typeDef.getAttribute("typeName")]
    #if symbolType.size > parent.app.enums.MAX_RAM_SYMBOL_SIZE :
    if self.isPointer :
      symbolType = nescDecls.nescPointer(parent.app, symbolType)
    if self.isArray :
      if length % symbolType.size == 0 :
        numElements = length // symbolType.size
      else :
        raise Exception("Could not discern ram symbol array length")
      symbolType = nescDecls.nescArray(numElements, symbolType)
    structArgs = []
    if type(symbolType) == nescDecls.nescStruct :
      self.isStruct = True
      structArgs.append(symbolType)
    else :
      structArgs.append(xmlDefinition.getAttribute("name"))
      structArgs.append( ("value", symbolType) )
    #now initialize this command as a TosMsg object (which is really a nescStruct)
    RoutingMessages.RoutingMessage.__init__(self, parent, 0, *structArgs)
    if self.isStruct :
      self.__dict__["name"] = xmlDefinition.getAttribute("name")
    if length != self.size :
        raise Exception("Ram symbol size incorrect")
    self.pokeResponseMsg = nescDecls.TosMsg(self.memAddress, "PokeResponseMsg",
                                          ("value", parent.app.types.result_t))
   


  def poke(self, value=None, arrayIndex = None, dereference=False, **nameArgs) :
    if not self.parent.app.__dict__.has_key("RamSymbolsM") :
      raise Exception("You must include the contrib/hood/tos/lib/RamSymbols/RamSymbolsM module in your nesc application in order to poke or peek at ram symbols")
    func = self.parent.app.RamSymbolsM.poke
    if arrayIndex != None :
      if self.isArray :
        if dereference == True :
          if self.isPointer:
            ptr = self.value["value"].elementType
            newValue = deepcopy(ptr.value)
            func.symbol.memAddress = self.memAddress + ptr.size * arrayIndex
            func.symbol.length = newValue.size
          else :
            raise Exception("Dereferencing is only allowed for pointer types")
        else :
          newValue = deepcopy(self.value["value"].elementType)
          func.symbol.memAddress = self.memAddress + newValue.size * arrayIndex
          func.symbol.length = newValue.size
      else :
        raise Exception("Indexing a poke is only supported for arrays")
    elif dereference == True :
      if self.isPointer and self.isArray :
        raise Exception("Poke cannot be used to dereference an entire array of pointers")
      elif not self.isPointer :
        raise Exception("Dereferencing is only allowed for pointer types")
      newValue = deepcopy(self.value["value"].value)
      func.symbol.memAddress = self.memAddress
      func.symbol.length = newValue.size
    else : 
      if self.isArray and self.size > self.parent.app.ramSymbol_t.data.size :
          raise Exception("Array is too large for poking.  You must index the poke")
      if self.isStruct :
        newValue = deepcopy(self)
      elif self.isPointer :
        newValue = self.parent.app.types["unsigned int"]
      else :
        newValue = deepcopy(self.value["value"])
      func.symbol.memAddress = self.memAddress
      func.symbol.length = newValue.size
    if func.symbol.length > self.parent.app.types.ramSymbol_t.data.size :
      raise Exception("Ram symbol size too large for msg buffer")
    if value != None :
      self._assignParam(newValue, value, "value")
    newBytes = newValue.getBytes()
    oldBytes = func.symbol.data.getBytes()
    newBytes = oldBytes.replace(oldBytes[:func.symbol.length], newBytes, 1)
    func.symbol.data.setBytes(newBytes)
    func.symbol.dereference = dereference
    result = func(**nameArgs)
    return map(self.parsePokeResponse, result)
    
  def parsePokeResponse(self, msg) :
    response = deepcopy(self.pokeResponseMsg)
    if msg.nescType == "RpcResponseMsg":
      response.value=0
      addr = msg.sourceAddress
    else :
      if msg.value["value"].value != self.memAddress and (not self.isArray or
          (msg.value["value"].value -self.memAddress) % self.value["value"].elementType.size !=0) :
        raise Exception("Memory address mismatch in poke response")
      response.value = 1
      addr = msg.parentMsg.sourceAddress
    response.parentMsg = msg
    response.nescType = "".join( [response.nescType, ",  nodeID=%d"%addr] )
    return response




  def peek(self, arrayIndex = None, dereference=False, **nameArgs) :
    if not self.parent.app.__dict__.has_key("RamSymbolsM") :
      raise Exception("You must include the contrib/hood/tos/lib/RamSymbols/RamSymbolsM module in your nesc application in order to poke or peek at ram symbols")
    func = self.parent.app.RamSymbolsM.peek
    if arrayIndex != None :
      #change memaddress to memAddres + array index
      if self.isArray :
        if dereference :
          if self.isPointer :
            func.memAddress = self.memAddress + self.value["value"].elementType.size * arrayIndex
            #set length of memcpy to ptr dereferenced value
            func.length = self.value["value"].elementType.value.size
          else :
            raise Exception("Dereferencing a peek is only allowed for pointers")
        else :
          func.memAddress = self.memAddress + self.value["value"].elementType.size * arrayIndex
          func.length = self.value["value"].elementType.size
      else :
        raise Exception("Indexing a peek is only allowed for arrays")
    elif dereference :
      #if this is an array or ptrs, fail
      if self.isArray :
        raise Exception("peek cannot be used to dereference an array of pointers")
      func.memAddress = self.memAddress
      func.length = self.value["value"].size
    else :
      #if this is an array check if the whole thing will fit in the return msg
      if self.isArray and self.size > self.parent.app.types.ramSymbol_t.data.size :
        raise Exception("Array is too large for peeking.  You must index the peek")
      func.memAddress = self.memAddress
      func.length = self.size
    if func.length > self.parent.app.types.ramSymbol_t.data.size :
      raise Exception("Ram symbol size too large for msg buffer")
    func.dereference = dereference
    result = func(**nameArgs)
    return map(self.parsePeekResponse, result)

  def parsePeekResponse(self, msg) :
    #create the response message depending on if was rpc error or not
    if msg.nescType == "RpcResponseMsg":
      response = nescDecls.TosMsg(self.memAddress, "PeekErrorMsg",
                                  ("value", self.parent.app.types.result_t))
      response.value=0
      addr = msg.sourceAddress
    else:
      #choose the type depending on if the ramSymbol is the entire symbol or element of array
      if msg.length == self.size and msg.memAddress == self.memAddress :
        if self.isStruct :
          value = deepcopy(self)
        else :
          value = deepcopy(self.value["value"])
      elif (self.isArray and msg.length == self.value["value"].elementType.size and
            (msg.memAddress -self.memAddress) % self.value["value"].elementType.size ==0):
        value = deepcopy(self.value["value"].elementType)
      elif (self.isArray and self.isPointer
            and msg.length == self.value["value"].elementType.value.size and
            (msg.memAddress -self.memAddress) % self.value["value"].elementType.value.size ==0):
        value = deepcopy(self.value["value"].elementType.value)
      elif (self.isPointer
            and msg.length == self.value["value"].value.size and
            (msg.memAddress -self.memAddress) % self.value["value"].value.size ==0):
        value = deepcopy(self.value["value"].value)
      else :
        raise Exception("Memory address mismatch in peek response")
      #choose the type depending on whether calling func was peek or ptrPeek
      if self.isPointer :
        if msg.dereference :
          value = value.value
        else :
          value = self.parent.app.types["unsigned int"]
      #create the return message from type depending if it is a struct already or must be created
      if issubclass(type(value), nescDecls.nescStruct) :
        response = nescDecls.TosMsg(self.memAddress, value)
      else :
        response = nescDecls.TosMsg(self.memAddress, value.nescType,
                                    ("value", value))
      bytes = msg.data.getBytes()
      response.setBytes(bytes[:response.size])
      addr = msg.parentMsg.sourceAddress
    response.parentMsg = msg
    response.nescType = "".join( [response.nescType, ",  nodeID=%d"%addr])
    return response
  
  def __str__(self) :
    if self.isStruct :
      return "%20s : %s\n" % (self.nescType,self.name)
    else:
      return "%20s : %s\n" % (self.value["value"].nescType,self.nescType)

        
  def __deepcopy__(self, memo={}) :
    result = self.__class__()
    memo[id(self)] = result
    result.parent = self.parent
    for (callParam, defaultVal) in self.parent.defaultCallParams :
      result.__dict__[callParam] = deepcopy(self.__dict__[callParam], memo)
    nescDecls.TosMsg.__init__(result, self.amType, self)
    return result    
    



  
class RamSymbols( RoutingMessages.RoutingMessages) :
  """A container class from which to find all ram symbols.

  """
  
  def __init__(self, app, buildDir, sendComm=None, receiveComm=None, tosbase=True, **callParams) :
    """ Find function defs in rpcSchema.xml file and create function objects."""
    if not "ramSymbol_t" in app.types._types :
      print "The contrib/hood/tos/lib/RamSymbols/RamSymbolsM module was not included.  No ram symbols will be imported."
    RoutingMessages.RoutingMessages.__init__(self, app, sendComm, receiveComm,
                             app.enums.AM_RPCCOMMANDMSG, tosbase)

    self.defaultCallParams = ( ("address", None), ("returnAddress", None),
                        ("timeout", 1), ("blocking", True), ("responseDesired", True) )
    self.initializeCallParams(callParams)
    self.tooLarge = []
    self.sizeIncorrect = []
    self.noType = []
    self.arraySizeIncorrect = []
    nescDeclsXml = nescDecls.findBuildFile(buildDir, "nescDecls.xml")
    schema = minidom.parse(nescDeclsXml)
    symbols, = schema.childNodes[0].getElementsByTagName("ramSymbols")
    symbols = [node for node in symbols.childNodes if node.nodeType == 1]
    for symbolDef in symbols: 
      try :
          self._messages[symbolDef.getAttribute("name")] = RamSymbol(symbolDef, self)
      except Exception, e:
          if len(e.args) > 0 and e.args[0].find("No type") == 0:
              self.noType.append(symbolDef)
          elif len(e.args) > 0 and e.args[0].find("Could not discern") == 0:
              self.arraySizeIncorrect.append(symbolDef)
          elif len(e.args) > 0 and e.args[0].find("Ram symbol size too large") == 0:
              self.tooLarge.append(symbolDef)
          elif len(e.args) > 0 and e.args[0].find("Ram symbol size incorrect") == 0:
              self.sizeIncorrect.append(symbolDef)
          else :
              raise
    #self.printSkippedSymbols()
      
  def printSkippedSymbols(self) :
    err = ""
    if len(self.tooLarge) >0 :
      err += "\nWarning: %d ram symbols were too large for %d byte packet.\n" % (len(self.tooLarge), self.app.types.ramSymbol_t.data.size )
      for symbol in self.tooLarge :
        err += "\t%s\n" % symbol.getAttribute("name")
    if len(self.sizeIncorrect) >0 :
      err += "\nWarning: The size of the following ram symbols does not match the size of the discovered type:\n"
      for symbol in self.sizeIncorrect :
        err += "\t%s\n" % symbol.getAttribute("name")
    if len(self.noType) >0 :
      err += "\nWarning: No type was found for the following ram symbols:\n"
      for symbol in self.noType :
        err += "\t%s\n" % symbol.getAttribute("name")
    if len(self.arraySizeIncorrect) >0 :
      err += "\nWarning: The following ram symbols are arrays with length not a multiple of the type size:\n"
      for symbol in self.arraySizeIncorrect :
        err += "\t%s\n" % symbol.getAttribute("name")
    if len(err) > 0 : print err

--- NEW FILE: Rpc.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 
#

"""\
Rpc.py -- a tool for calling rpc functions on motes.
To be used in conjunction with tinyos-1.x/contrib/hood/tos/lib/Rpc.

rpc = Rpc(\"./apps/TestRpc/build/pc/foo.xml\")
rpc.redOn()
rpc.redOff()
rpc.myfunc(101,'abc')

or

f=rpc.myfunc
f.firstParam = 101
f.secondParam = 'abc'
f()
"""

import sys, string, time, types
from xml.dom import minidom
import pytos.util.nescDecls as nescDecls
import pytos.util.RoutingMessages as RoutingMessages
import pytos.Comm as Comm 
from copy import deepcopy

class RpcFunction( RoutingMessages.RoutingMessage ) :
  """a callable TosMsg used to interact with the rpc.nc module.  On
  being called, either with positional or named arguments, this
  object will fill in the params given, pack up a message and send
  it off using comm.send.  Named arguments can have the names of the
  param names.  Any params not specified will have default values as
  specified by the initialization semantics of nescDecls.nescStruct
  (zero).  Optional named parameter \"comm\" can be used to pass
  arguments to the comm.send function.
  
  usage:
  rpcFunction.param = X
  rpcFunction()
  rpcFunction(X, Y, Z)
  rpcFunction(p1=X, p2=Y, p3=Z)
  rpcFunction(X, p3=Z, p2=Y)
  rpcFunction(X, p3=Z, p2=Y, comm=['COM1','COM2',...])
  """

  #this is the variable the determines, on a blocking rpc call, how
  #many messages will be queued up.  Should perhaps be bigger for large
  #networks, but will generally be the same number for all rpc
  #functions that use the same send and receive comm stacks.
  msgQueueSize = 10
  
  def __init__(self, xmlDefinition=None, parent=None) :
    if xmlDefinition==None :
      return
    self.responseMsg = None
    #start creating the arguments to the nescStruct constructor.
    #first, add the command msg name and the rpc headers
    structArgs = []
    structArgs.append(xmlDefinition.tagName)
    structArgs.append( ("rpcHeader", parent.app.types.RpcCommandMsg) )
    #then, add each parameter as a new msg field 
    for i in range(int(xmlDefinition.getAttribute("numParams"))) :
      param = xmlDefinition.getElementsByTagName("param%d" % i)
      paramName = param[0].getAttribute("name")
      paramType = parent.app.types[
        param[0].getElementsByTagName("type")[0].getAttribute("typeName")]
      structArgs.append( (paramName, paramType) )
    #now initialize this command as a TosMsg object (which is really a nescStruct)
    RoutingMessages.RoutingMessage.__init__(self, parent,
                                            parent.app.enums.AM_RPCCOMMANDMSG, *structArgs)
    #fill in the header fields once and for all
    self.rpcHeader.commandID = int(xmlDefinition.getAttribute("commandID"))
    self.rpcHeader.dataLength = self.size - self.rpcHeader.size
    #turn the response type into a response msg (to be received by the user)
    responseType = parent.app.types[
      xmlDefinition.getElementsByTagName("returnType")[0].getAttribute("typeName")]
    if issubclass(type(responseType), nescDecls.nescStruct) :
      self.responseMsg = nescDecls.TosMsg(self.rpcHeader.commandID, responseType)
    else :
      self.responseMsg = nescDecls.TosMsg(self.rpcHeader.commandID, responseType.nescType,
                                          ("value", responseType))

  def __call__(self, *posArgs, **nameArgs) :
    if not self.parent.app.__dict__.has_key("RpcM") :
      raise Exception("You must include the contrib/hood/tos/lib/Rpc/RpcM module in your nesc application in order to use rpc commands")
    commArgs = ()
    callParams = self.parseCallParams(nameArgs)
    self.rpcHeader.transactionID+=1
    thisCall = deepcopy(self)
    thisCall.rpcHeader.address = callParams["address"]
    thisCall.rpcHeader.returnAddress = callParams["returnAddress"] 
    thisCall.rpcHeader.responseDesired = callParams["responseDesired"]

    #If this is a blocking call, get ready to process response msgs for timeout time
    if callParams["blocking"] \
           and callParams["timeout"] > 0 \
           and callParams["responseDesired"]==True:
      responseQueue = Comm.MessageQueue(self.msgQueueSize)
      self.register(responseQueue)
      processMsgs = True

    thisCall._send(callParams["address"], *posArgs, **nameArgs)

    if processMsgs :
      startTime = time.time()
      responses = []
      while time.time() - startTime <= callParams["timeout"] :
        try:
          (addr,msg) = responseQueue.get(True, 0.1) #why 0.1?
          if msg.nescType == "RpcResponseMsg" :
            rxdTransactionID = msg.transactionID
          else :
            rxdTransactionID = msg.parentMsg.transactionID
          if rxdTransactionID == thisCall.rpcHeader.transactionID :
            responses.append(msg)
            print '.',
        except Exception, e:
          if len(e.args) >0 :
            print "rpc error: %s" % str(e)
      print ' '
      self.unregister(responseQueue)
      return responses
    
  def __str__(self) :
    """print function signature"""
    string = "%21s %s( " % (self.responseMsg.nescType, self.nescType)
    for field in self.fields[1:] :
      string += " %s %s," % ( self.value[field["name"]].nescType,
                              field["name"] )
    if len(self.fields) >0 :
      string += "\b"
    string += " )\n"
    return string

  def __deepcopy__(self, memo={}) :
    result = self.__class__()
    memo[id(self)] = result
#    result.rpcHeader = deepcopy(self.rpcHeader, memo)
    result.responseMsg = deepcopy(self.responseMsg, memo)
    result.parent = self.parent
    for (callParam, defaultVal) in self.parent.defaultCallParams :
      result.__dict__[callParam] = deepcopy(self.__dict__[callParam], memo)
    nescDecls.TosMsg.__init__(result, self.amType, self)
    return result    
    
  def printCurrentValues(self) :
    print nescDecls.TosMsg.__str__(self)
                    
  def register(self, listener, comm=()) :
    self.parent.receiveComm.register(self.parent.app.msgs.RpcResponseMsg,
                              RpcResponseListener(self.parent.app, self.responseMsg, listener), *comm)
    
  def unregister(self, listener, comm=()) :
    self.parent.receiveComm.unregister(self.parent.app.msgs.RpcResponseMsg,
                                RpcResponseListener(self.parent.app, self.responseMsg, listener), *comm)




class RpcResponseListener( Comm.MessageListener ):

  def __init__(self, app, responseMsg, callback ):
    self.app = app
    self.responseMsg = responseMsg
    Comm.MessageListener.__init__(self, callback)

  def messageReceived( self , addr , msg ) :
    if msg.commandID == self.responseMsg.amType :
      if msg.errorCode == self.app.enums.RPC_SUCCESS :
        response = deepcopy(self.responseMsg)
        response.setBytes( msg.data.getBytes() )
        response.parentMsg = msg
        response.nescType = "".join( [response.nescType,
                                      ",  nodeID=%d"%response.parentMsg.sourceAddress] )
        self.callback( addr, response )
      else :
        self.callback( addr, msg)
          

  
  
class Rpc( RoutingMessages.RoutingMessages) :
  """A container class from which to call all rpc functions.

  usage:
    rpc = Rpc(sendComm, receiveComm, '/path/to/rpcSchema.xml')
    print rpc
    rpc.module.interface.function(args)
    rpc.module.function(args)
  """
  
  def __init__(self, app, buildDir, sendComm=None, receiveComm=None, tosbase=True, **callParams) :
    """ Find function defs in rpcSchema.xml file and create function objects."""
    RoutingMessages.RoutingMessages.__init__(self, app, sendComm, receiveComm,
                             app.enums.AM_RPCCOMMANDMSG, tosbase)

    self.defaultCallParams = ( ("address", None), ("returnAddress", None),
                        ("timeout", 1), ("blocking", True), ("responseDesired", True) )
    self.initializeCallParams(callParams)

    rpcSchema = nescDecls.findBuildFile(buildDir, "rpcSchema.xml")
    schema = minidom.parse(rpcSchema)
    functions, = schema.childNodes[0].getElementsByTagName("rpcFunctions")
    functions = [node for node in functions.childNodes if node.nodeType == 1]
    for funcDef in functions: 
      self._messages[funcDef.tagName] = RpcFunction(funcDef, self)


--- 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 
#





More information about the Tinyos-commits mailing list