Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I am writing a server that serves an EventSource data-stream using Python's Twisted. Although this is my first time using Twisted, I hope this part of the code is acceptable.

To get the events, the code queries another server. The other server is a PLC that has its own special ASCII-based protocol. It must be continually polled in order to extract the changes in variables that I would like to turn into events. It is here that I am a little uncertain of the best technique, and I have not been able to find any examples of anything similar.

Specifically, my question is: should the ClientFactory keep a reference to the instance of the Protocol that it creates? I am using this instance in my code in order to have access to the protocol, which allows me to query the PLC.

EDIT: Just to clarify, the PLC can accept only one connection at a time. Thus the code needs to be able to send all queries to the PLC through just the one connection.

Although this approach works, it feels like a bit of a hack to me as the "factory" is really being coerced into something more like a Singleton. Is this how it should be done? Is there a better approach?

To make this more concrete, here is a section of the EventSource code that uses the instance of the protocol:

class EventSource(Resource):
    isLeaf = True
    def __init__(self):
        # Create connection to PLC
        self.factory = ASCIIClientFactory()
        reactor.connectTCP("192.168.0.91", 10001, self.factory)


    def writeEvent(self, request):
        if self.factory._instance is None:
            return # nothing we can do yet

        # Obtain variables from query string
        response = {}
        if "x" in request.args:
            address = request.args["x"][0]
            d = self.factory._instance.getRegister( address ) # <--- What do you think?
            def onResult(data):
                response["x"] = data
                request.write("\nevent:\n")
                request.write("data: "+json.dumps(response)+"\n")
            d.addCallback(onResult)

And for reference, here is the protocol and its ClientFactory:

class ASCIIClientProtocol(LineReceiver):
    class Command:
        def __init__(self, string):
            self.string = string
            self.deferred = Deferred()

    def __init__(self):
        self._busy = False
        self._commands = deque()
        self._current = None

    def processCommand(self):
        if not self._busy:
            self._busy = True
            self._current = self._commands.popleft()
            self.transport.write( self._current.string )
            return self._current.deferred

    def addCommand(self, commandString):
        command = ASCIIClientProtocol.Command( commandString )
        self._commands.append( command )
        self.processCommand()
        return command.deferred

    def getRegister(self, address):
        return self.addCommand("SR"+str(address)+"*\n")

    def getRaw(self, address):
        return self.addCommand("SU"+str(address)+"*\n")

    def setRegister(self, address, value):
        return self.addCommand("SW"+str(address)+" "+str(value)+"*\n")

    def dataReceived(self, data):
        assert self._current != None

        self._current.deferred.callback(data[0:-2])  # Remove the trailing \r\n
        self._busy = False
        self._current = None
        if len(self._commands) != 0:
            self.processCommand()

class ASCIIClientFactory(protocol.ClientFactory):
    def __init__(self):
        self._instance = None

    def buildProtocol(self, addr):
        self._instance = ASCIIClientProtocol()
        return self._instance

    def clientConnectionFailed(self, connector, reason):
        print "Failed to connect to PLC"
        print "reason:", reason
        print "Trying again to connect..."
        connector.connect()

    def clientConnectionLost(self, connector, reason):
        print "Connection to PLC has been lost"
        print "reason:", reason
        print "Trying to reconnect..."
        connector.connect()

    def startedConnecting(self, connector):
        print "Connecting to PLC..."
share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.