Python-Twisted Function Calls

This is the place for queries that don't fit in any of the other categories.

Python-Twisted Function Calls

Postby cp3 » Thu Feb 06, 2014 7:53 pm

I am wanting to add some funtionality to a piece of existing code. The program uses Python-Twisted to communicate with Modbus devices on a polling interval. I want to add on-demand polls so that a message does not have to wait on a poll interval to send. I have a function called sendNow() that uses a looping call and checks the status of variable and will try and call the sendMessage function when this variable is true. I am looking for some insight on what I am doing wrong with my function call of sendMessage. I have tried several different ways to call it. The one shown in the code currently gives an error: Here is a simplified version of the code: global name self is not defined.

Code: Select all
from twisted.internet import protocol, reactor, task
from twisted.internet.task import LoopingCall
from datetime import datetime
from twisted.web.resource import Resource
from twisted.web.server import NOT_DONE_YET

import MBDataTable
from mbprotocols import ModbusTCPLib
from mbprotocols import ModbusDataLib


#############################################################################

########################################################
def WriteFaultToDT(FaultCode, containerinfo):
   """Write a communications or connection fault to the data table.
   Parameters: FaultCode (string) - The fault code to record. This will be
      converted to an integer before writing it to the data table registers.
   containerinfo (object) = The container object. This is used to convert the
      fault code to an integer and to obtain the fault addresses.
   """
   # Get the container address info.
   FaultCoilAddr, FaultInpAddr, FaultHoldingRegAddr, FaultInpRegAddr, freset = containerinfo.GetFaultAddresses()

   regvalue = containerinfo.FaultCodeToInt(FaultCode)

   # Update the memory table with the exception.
   MBDataTable.MemMap.SetCoilsBool(FaultCoilAddr, True)
   MBDataTable.MemMap.SetDiscreteInputsBool(FaultInpAddr, True)
   MBDataTable.MemMap.SetHoldingRegistersInt(FaultHoldingRegAddr, regvalue)
   MBDataTable.MemMap.SetInputRegistersInt(FaultInpRegAddr, regvalue)

##############################################################################




##############################################################################

#
class ModbusClientProtocol(protocol.Protocol):
   """Implements the Modbus/TCP client protocol for the Twisted Framework.
   """

   ########################################################
   def __init__(self, connection):
      """Parameters: connection - This is a client connection container object
      which is used to store all the connection configuration
      information. This is a complex object which is created as part
      of the configuration process. It contains all the information
      the client needs to run and poll remote servers.
      """
      self._ConnectionInfo = connection
      # ID code used to track reactor.callLater schedules.
      self.CallID = None
      # ID code used to track reactor.callLater schedules.
      self.RetryCallID = None
      # If True, we are at the end of the command list.
      self.EndofCommandList = False

      # Client message creation.
      self._MBClientMsg = ModbusTCPLib.MBTCPClientMessages()

      # Transaction ID sent and expected.
      self._TransID = 0
      # Unit ID
      self._UnitID = 1
      
      self._CmdName = 'default'
      self._FunctionCode = 0
      self._AddrRequested = 0
      self._QuantityRequested = 0
      self._MemAddr = 0


      # Get the fault addresses. However, if the configuration
      # is invalid, these will be default vaules and not usable.
      self._FaultInpAddr, self._FaultCoilAddr, self._FaultInpRegAddr, \
         self._FaultHoldingRegAddr, self._FaultResetAddr \
         = self._ConnectionInfo.GetFaultAddresses()

      # Limit on the number of consecutive transaction ID errors before
      # reporting a fault.
      self._TIDLimit = 3
      # Counter for transaction ID error filtering.
      self._TIDError = 0
      print "MP __init__"
   ########################################################
   #
   def NextRequest(self):
      """Increment the command list and execute the next command.
      This includes constructing the next message.
      """

      # Transaction ID is incremented for each request to check message integrity.
      # It must not exceed the maximum for an 16 bit unsigned int though.
      self._TransID += 1
      if (self._TransID > 65535):
         self._TransID = 1


      # Get the parameters for this command.
      self._CmdName, self._FunctionCode, self._AddrRequested, self._QuantityRequested, \
         self._MemAddr, self._UnitID, EndofList = self._ConnectionInfo.NextCommand()

      # Get message data appropriate for the function being executed.
      if self._FunctionCode in (1, 2, 3, 4):
         MsgData = ''         # No message data is sent for a read.
      elif (self._FunctionCode == 5):
         CoilData = MBDataTable.MemMap.GetCoilsBool(self._MemAddr)
         if CoilData:
            MsgData = ModbusDataLib.coilvalue(1)   # Special format for function 5
         else:
            MsgData = ModbusDataLib.coilvalue(0)   # Special format for function 5
      elif self._FunctionCode in (6, 16):
         MsgData = MBDataTable.MemMap.GetHoldingRegisters(self._MemAddr, self._QuantityRequested)
      elif (self._FunctionCode == 15):
         MsgData = MBDataTable.MemMap.GetCoils(self._MemAddr, self._QuantityRequested)
      else:
         return '', True      # Invalid function code.


      # Create and return message.
      try:
         Message = self._MBClientMsg.MBRequest(self._TransID, self._UnitID, self._FunctionCode,
               self._AddrRequested, self._QuantityRequested, MsgData)
      except:
         Message = ''

      return   Message, EndofList

      print "Next Request"
   


   ########################################################
   #
   def dataReceived(self, ServerReply):
      """Called automatically when the server replies.
       Parameters: ServerReply - This is the raw binary Modbus
          message response.
      """
      # Cancel the pending "retry" call.
      if (self.RetryCallID.cancelled != 1):
         self.RetryCallID.cancel()

      # Reply from server.
      self.HandleReply(ServerReply)

      # Record the current status.
      self._ConnectionInfo.SetConStatusRunning()

      # Set up the next data poll.
      if not self.EndofCommandList:
         # Time delay between individual commands in the list.
         delaytime = self._ConnectionInfo.GetCommandTime()
      else:
         # Time delay between sets of commands.
         delaytime = self._ConnectionInfo.GetRepeatTime()

      self.CallID = reactor.callLater(delaytime, self.sendMessage)
      print "data Recieved"
   
   ########################################################
   def sendMessage(self):#the function I want to call on demand
      """Called to send a message. The call is set up by an initial
      "callLater" delayed call, and then subsequently after each
      server reply. There is also an additional delayed call which is
      set up after each message is sent. If the server does not reply,
      then this second call will ensure that another request is sent.
      """
      print "send Message"
      # Get the next command in the list.
      RequestMsg, self.EndofCommandList = self.NextRequest()
      
      # Set up a retry in case the server doesn't reply.
      delaytime = self._ConnectionInfo.GetRetryTime()
      self.RetryCallID = reactor.callLater(delaytime, self.SendTimeOut)
 
      if (RequestMsg != ''):
         self.transport.write(RequestMsg)   # Send the message!
      else:
         # We shouldn't get here unless there is a bug
         # in the software.
         if (self.RetryCallID.cancelled != 1):
            self.RetryCallID.cancel()
         self.transport.loseConnection()
##########The added function#################3   
   def sendNow():
      MyCoilData = MBDataTable.MemMap.GetCoilsBool(32100)#Memory location to monitor
      if (MyCoilData !=0):
         print "Immediate Request"
         delaytime = 1
         reactor.callLater(delaytime, self.sendMessage)#call the sendMessage function
   lc = LoopingCall(sendNow)#Run sendNow every 3s
   lc.start(3)
Last edited by Yoriz on Thu Feb 06, 2014 8:27 pm, edited 1 time in total.
Reason: First post lock
cp3
 
Posts: 2
Joined: Thu Feb 06, 2014 7:51 pm

Re: Python-Twisted Function Calls

Postby Yoriz » Thu Feb 06, 2014 8:34 pm

Hi welcome to the forum be sure to read the 'new users, read this' link in my signature.

If
Code: Select all
def sendNow():
is meant to be a method of
Code: Select all
ModbusClientProtocol
it's first parameter should be self
Code: Select all
def sendNow(self):

Code: Select all
   def sendNow():
      MyCoilData = MBDataTable.MemMap.GetCoilsBool(32100)#Memory location to monitor
      if (MyCoilData !=0):
         print "Immediate Request"
         delaytime = 1
         reactor.callLater(delaytime, self.sendMessage)#call the sendMessage function
   lc = LoopingCall(sendNow)#Run sendNow every 3s
   lc.start(3)
indentation is wrong on the last two lines.

Now i've got to the end it looks like you want a function not a method, so you need to removed the indentation so sendNow is not part of ModbusClientProtocol
and in this line
Code: Select all
 reactor.callLater(delaytime, self.sendMessage)#call the sendMessage function
self is not defined because its a function not a method of a class.
You'll need to create an instance of ModbusClientProtocol to call its sendMessage method
New Users, Read This
Join the #python-forum IRC channel on irc.freenode.net!
Spam topic disapproval technician
Windows7, Python 2.7.4., WxPython 2.9.5.0., some Python 3.3
User avatar
Yoriz
 
Posts: 789
Joined: Fri Feb 08, 2013 1:35 am
Location: UK

Re: Python-Twisted Function Calls

Postby cp3 » Thu Feb 06, 2014 11:32 pm

I kindly thank you for your information. I made the changes you mentioned but I am still getting an error. This is the chagned code:
Code: Select all
def sendNow(self):
   myInstance = ModbusClientProtocol(self._ConnectionInfo)#creating an instance of ModbusClientProtocol   
   MyCoilData = MBDataTable.MemMap.GetCoilsBool(32100)#Memory location to monitor
   print "In SendNow"
   if (MyCoilData !=0):
      print "Immediate Request"
      delaytime = 1
      reactor.callLater(delaytime, myInstance.sendMessage)#call the sendMessage function
lc = LoopingCall(sendNow)#Run sendNow every 3s
lc.start(3)


This is the traceback that mentions I am missing an argument:
Code: Select all
Traceback (most recent call last):
  File "./mblogic/mbmain.py", line 70, in <module>
    import MBClient
  File "/home/cprice/mblogic_all/mblogic_2011-04-16/mblogic/MBClient.py", line 366, in <module>
    lc.start(3)
  File "/usr/lib/python2.7/dist-packages/twisted/internet/task.py", line 163, in start
    self()
  File "/usr/lib/python2.7/dist-packages/twisted/internet/task.py", line 208, in __call__
    d = defer.maybeDeferred(self.f, *self.a, **self.kw)
--- <exception caught here> ---
  File "/usr/lib/python2.7/dist-packages/twisted/internet/defer.py", line 134, in maybeDeferred
    result = f(*args, **kw)
exceptions.TypeError: sendNow() takes exactly 1 argument (0 given)


You mentioned that the last 2 lines are indented wrong. For the looping call to work I think they need to be in line with the def sendNow(self), but maybe I am missing something here.
cp3
 
Posts: 2
Joined: Thu Feb 06, 2014 7:51 pm

Re: Python-Twisted Function Calls

Postby Yoriz » Fri Feb 07, 2014 12:04 am

The indentation comment was because i was confused as to you wanting a function or a method because your code was a mixture of both.
The error you have now is because you have
Code: Select all
def sendNow(self):
so sendNow expects to be passed one parameter which you are using inside the function in this line
Code: Select all
 myInstance = ModbusClientProtocol(self._ConnectionInfo)#creating an instance of ModbusClientProtocol   

when you are calling senNow in this line
Code: Select all
lc = LoopingCall(sendNow)#Run sendNow every 3s
you dont pass a argument to it.
New Users, Read This
Join the #python-forum IRC channel on irc.freenode.net!
Spam topic disapproval technician
Windows7, Python 2.7.4., WxPython 2.9.5.0., some Python 3.3
User avatar
Yoriz
 
Posts: 789
Joined: Fri Feb 08, 2013 1:35 am
Location: UK


Return to General Coding Help

Who is online

Users browsing this forum: Basilius Sapientia, micseydel and 2 guests