1

我正在为现有软件编写一个插件,该插件将使用 twisted 与 Denon AV 接收器进行通信。我在我的协议中创建了一个方法 powerDenonOff,用于关闭设备,并在应用程序通过 runConcurrentThread 启动时在线程中启动反应器。我还有另一种方法,powerOff,当用户在软件中选择关闭设备时将调用该方法。我无法弄清楚如何从 Plugin 类调用协议中的 powerDenonOff 方法。

from twisted.internet.protocol import Factory, Protocol
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor

class DenonProtocol(LineReceiver):
    delimiter='\r'

    def lineReceived(self, line):
        pass

    def connectionMade(self):
        pass

    def powerDenonOff(self):
        self.transport.write("PWSTANDBY")

    def __call__(self):
        pass

class DenonFactory(Factory):

    def __init__(self):
        pass

    def startedConnecting(self, connector):
        pass

    def clientConnectionFailed(self, one, two):
        pass

    def connectionMade(self):
        pass

    def buildProtocol(self, addr):
        return DenonProtocol()

class Plugin(software.PluginClass):

    def powerOff(self):
        reactor.callInThread(powerDenonOff) #I think this may need to be callFromThread but
                                            #but I left it since that was where the poorly
                                            #worded version of my question left off.

    def runConcurrentThread(self):
        try:
            while True:
                #hardcoded for testing
                port = 23
                host = "111.11.11.11"

                denon=DenonFactory()
                reactor.connectTCP(host, port, denon)
                reactor.run()
        except self.StopThread:
            pass

    def stopConcurrentThread(self):
        reactor.callFromThread(reactor.stop)

如何在我的协议类中使用该方法?有没有更好的方法来实现这一点?

谢谢

4

1 回答 1

1

You can't call reactor.run more than once, so the loop in runConcurrentThread won't work. You need to start the reactor exactly once per process. You can call connectTCP any number of times and they'll work as long as the reactor gets started at some point.

If runConcurrentThread is called in a non-main thread, then you also need to start the reactor without child process support (child processes are only supported when the reactor is run in the main thread).

If stopConcurrentThread is called in the same thread as runConcurrentThread then it doesn't need to use reactor.callFromThread. However, if runConcurrentThread is supposed to block until the plugin is done, then there's no way stopConcurrentThread could be called in the same thread, so the use of reactor.callFromThread must be correct.

reactor.run will never raise StopThread. It will just return.

As far as how to call methods on the protocol instance "from" the plugin, this is just a question about managing references in Python. Recall that protocol instances are given a reference to their factory (your factory doesn't do this, but it could) and your plugin class is the code that creates the factory. This means you should be able to write plugin code that refers to the protocol via the factory.

However, you could also use one of the more convenient APIs - for example, endpoints:

endpoint = TCP4ClientEndpoint(reactor, host, port)
factory = ...
d = endpoint.connect(factory)
def connected(protocol):
    protocol.powerDenonOff()
    protocol.transport.loseConnection()
d.addCallback(connected)

This is just one possible approach - I don't know what kind of connection management makes sense to your application. If you want to maintain a single long-lived connection, then you might save the protocol reference for later use rather than calling powerDenonOff and then disconnecting it. I suspect that since the method here is for turning power off, the connection might not live much longer anyway though...

Crochet will help you do most of the more boring and finnicky parts of this. I suggest taking a look at it sooner rather than later.

For the rest, you just need to be very clear about what code runs in what thread. The only Twisted API you are allowed to use in a different thread than reactor.run was called in is reactor.callFromThread. You can use this to call other Twisted APIs in the reactor thread. Also keep in mind that you can only run one reactor per process, and you can only run it once. The rest is just keeping track of references to the objects you need to use.

于 2013-08-06T14:54:04.883 回答