3

I have written a test code which reads some coils/registers from a PLC's modbus server. When I call one request the code works. I unplugged the cable then Twisted calls clientConnectionLost function so my client will reconnected, when I plugged back the cable. If I do multiple requests, like in the code below, the handling breaks, nothing happens. I don't know what causes the problem.

#!/usr/bin/env python

from PyQt4 import QtCore, QtGui
from twisted.internet import reactor, protocol,defer
from pymodbus.constants import Defaults
from pymodbus.client.async import ModbusClientProtocol

from time import sleep

def logger():
    import logging
    logging.basicConfig()
    log = logging.getLogger()
    log.setLevel(logging.DEBUG)

logger()

class MyModbusClientProtocol(ModbusClientProtocol):

    def connectionMade(self):
        ModbusClientProtocol.connectionMade(self)
        print 'Connected'
        self.read()

    def read(self):
        deferred = self.read_coils(0,1999)
        deferred.addCallbacks(self.requestFetched,self.requestNotFetched)
        deferred = self.read_holding_registers(0,124)
        deferred.addCallbacks(self.requestFetched,self.requestNotFetched)

    def requestNotFetched(self,error):
        print error
        sleep(0.5)

    def requestFetched(self,response):
        try:
            print ("Fetched %d" % response.getRegister(1))
        except:
            print ("Fetched %d" % response.getBit(1))

        self.factory.counter += 1
        if self.factory.counter == 2:
            self.factory.counter = 0
            reactor.callLater(0,self.read)

class MyModbusClientFactory(protocol.ClientFactory):
    """A factory.

    A new protocol instance will be created each time we connect to the server.
    """
    def __init__(self):
        self.counter = 0

    def buildProtocol(self, addr):
        p = MyModbusClientProtocol()
        p.factory = self
        return p

    def clientConnectionLost(self, connector, reason):
        print "connection lost:", reason
        connector.connect()

    def clientConnectionFailed(self, connector, reason):
        print "connection failed:", reason
        connector.connect()

if __name__ == "__main__":

    factoryinstance = MyModbusClientFactory()

    reactor.connectTCP("192.168.2.69", 502, factoryinstance)

    reactor.run()
DanSut
  • 474
  • 8
  • 22
Poseidon
  • 73
  • 2
  • 5
  • I have a little trouble following the problem you're describing. Are you saying that if you issue two requests and then unplug the cable, `clientConnectionLost` is not called? This in contrast with the behavior when you issue only one request and then unplug the cable, which does cause `clientConnectionLost` to be called? – Jean-Paul Calderone Nov 29 '14 at 13:28
  • Yes, that's the problem, the two questions what you asked, is the two situation. I am reading the server in a loop with reactor callLater. I'm testing the reconnecting capability with unplugging the cable. With one deferred callback works. Two or more deferred callback not works. – Poseidon Nov 30 '14 at 14:13

1 Answers1

1

I have tested your code and believe that you have seen a timing related red herring when your code was seen to work after commenting out one of your requests. The behavior you are seeing where clientConnectionLost is not called is covered in the twisted FAQ: Why isn't my connectionLost method called?

What you need to do is create your own protocol specific timeout as you can't always rely on TCP's timeouts to work in your favor. A simple way to fix your code would be to add this to the end of your read method:

self.timeout = reactor.callLater(5, self.transport.abortConnection)

Which will abort the connection after 5 seconds wait. You also need to cancel this timeout when your requests have completed successfully with:

self.timeout.cancel()

in your requestFetched method before you call your read again.

DanSut
  • 474
  • 8
  • 22