Python Twisted deferToThread function is not working as expected - python

My goal here is to make a simple server handling TCP message like "process My String" that send "My String" to be processed by a quite time taking operation called (here slowFunction). Here I'm calling this function through deferToThread but nothing seems to happened : the defered's callback messages doesn't show up anywhere (breakpoints show it's never called) and the print in the function aren't displayed (breakpoints show it's never called)
from twisted import protocols
from twisted.protocols import basic
from twisted.internet import threads, protocol, reactor
from twisted.application import service, internet
import re
import time
def slowFunction(arg):
print "starting process"
time.sleep(1)
print "processed "+arg
class MySimpleServer(basic.LineReceiver):
PROCESS_COMMAND = "process (.*)" #re pattern
processFunction = slowFunction
clients = []
def connectionMade(self):
print "Client connected"
MySimpleServer.clients.append(self)
def connectionLost(self, reason):
print "Client gone"
MySimpleServer.clients.remove(self)
def onProcessDone(self):
self.message("Process done")
def onError(self):
self.message("Process fail with error")
def lineReceived(self, line):
processArgumentResult = re.search(MySimpleServer.PROCESS_COMMAND, line)
if not processArgumentResult == None:
processArgument = processArgumentResult.groups()[0]
deferred = threads.deferToThread(MySimpleServer.processFunction, processArgument)
deferred.addCallback(self.onProcessDone)
deferred.addErrback(self.onError)
self.message("processing your request")
else:
print "Unknown message line: "+line
def message(self, message):
self.transport.write(message + '\n')
if __name__ == '__main__':
factory = protocol.ServerFactory()
factory.protocol = MySimpleServer
factory.client = []
reactor.listenTCP(8000, factory)
reactor.run()

I've got some help by the guys of twisted irc
The points are : the callback (onProcessDone and onError) are supposed to take a result argument, and the function called by deferToThread will receive self as an argument (it should one of the MySimpleServer class' method).
The final code is now :
from twisted import protocols
from twisted.protocols import basic
from twisted.internet import threads, protocol, reactor
from twisted.application import service, internet
import re
import time
def slowFunction(arg):
print "starting process"
time.sleep(20)
print "processed "+arg
class MySimpleServer(basic.LineReceiver):
PROCESS_COMMAND = "process (.*)" #re pattern
clients = []
def connectionMade(self):
print "Client connected"
MySimpleServer.clients.append(self)
def connectionLost(self, reason):
print "Client gone"
MySimpleServer.clients.remove(self)
def onProcessDone(self, result):
self.message("Process done")
def onError(self, result):
self.message("Process fail with error")
def processFunction(self, processArgument):
slowFunction(processArgument)
def lineReceived(self, line):
processArgumentResult = re.search(MySimpleServer.PROCESS_COMMAND, line)
if not processArgumentResult == None:
processArgument = processArgumentResult.groups()[0]
deferred = threads.deferToThread(self.processFunction, processArgument)
deferred.addCallback(self.onProcessDone)
deferred.addErrback(self.onError)
self.message("processing your request")
else:
print "Unknown message line: "+line
def message(self, message):
self.transport.write(message + '\n')
if __name__ == '__main__':
factory = protocol.ServerFactory()
factory.protocol = MySimpleServer
factory.client = []
reactor.listenTCP(8000, factory)
reactor.run()

Another way you could've done this is with staticmethod; this is the only legitimate use for it.
class MySimpleServer(basic.LineReceiver):
processFunction = staticmethod(slowFunction)

Related

Limit number of users in a chatroom with a Twisted-based socket

I followed the following tutorial (http://www.raywenderlich.com/3932/how-to-create-a-socket-based-iphone-app-and-server) and I got the code you can see below. This code allows an unlimited number of clients to connect to a chat. What I want to do is to limit this number of clients so that no more than two users can chat at the same chatroom.
In order to do so, I actually just need to know one thing: how to get a unique identifier for every client. Which can be used later on, in the function for c in self.factory.clients: c.message(msg) in order to only send the message to the client I want.
I'd appreciate any contribution!
# Import from Twisted
from twisted.internet.protocol import Factory, Protocol
from twisted.internet import reactor
# IphoneChat: our own protocol
class IphoneChat(Protocol):
def connectionMade(self):
self.factory.clients.append(self)
print "Clients are ", self.factory.clients
def connectionLost(self, reason):
self.factory.clients.remove(self)
def dataReceived(self, data):
a = data.split(':')
print a
if len(a) > 1:
command = a[0]
content = a[1]
msg = ""
if command == "iam":
self.name = content
elif command == "msg":
msg = self.name + ": " + content
for c in self.factory.clients:
c.message(msg)
def message(self, message):
self.transport.write(message + '\n')
# Factory: handles all the socket connections
factory = Factory()
factory.clients = []
factory.protocol = IphoneChat
# Reactor: listens to factory
reactor.listenTCP(80, factory)
print "Iphone Chat server started"
reactor.run();
Try this: in connectionMade, if number of clients is already 2, close the new connection:
if len(self.factory.clients) == 2:
self.transport.loseConnection()

Python, Call a class function from another class

Can you anyone please help me (noob) call the broadcast function from class BroadcastServerFactory in class process, as per attached code
I have tried so many methods of call a function from another class, but no solution
import time, sys
from apscheduler.scheduler import Scheduler
import threading
import socket
from twisted.internet import reactor
from twisted.python import log
from twisted.web.server import Site
from twisted.web.static import File
from autobahn.websocket import WebSocketServerFactory, \
WebSocketServerProtocol, \
listenWS
class process(threading.Thread):
def __init__(self, buffer3):
threading.Thread.__init__(self)
self.setDaemon(True)
self.buffer3 = buffer3
def run(self):
factory.broadcast("I don't know what I'm doing!")
class BroadcastServerProtocol(WebSocketServerProtocol):
def onOpen(self):
self.factory.register(self)
def onMessage(self, msg, binary):
if not binary:
self.factory.broadcast("'%s' from %s" % (msg, self.peerstr))
def connectionLost(self, reason):
WebSocketServerProtocol.connectionLost(self, reason)
self.factory.unregister(self)
class BroadcastServerFactory(WebSocketServerFactory):
"""
Simple broadcast server broadcasting any message it receives to all
currently connected clients.
"""
def __init__(self, url, debug = False, debugCodePaths = False):
WebSocketServerFactory.__init__(self, url, debug = debug, debugCodePaths = debugCodePaths)
self.clients = []
self.tickcount = 0
self.tick()
def tick(self):
self.tickcount += 1
self.broadcast("'tick %d' from server" % self.tickcount)
reactor.callLater(1, self.tick)
def register(self, client):
if not client in self.clients:
print "registered client " + client.peerstr
self.clients.append(client)
def unregister(self, client):
if client in self.clients:
print "unregistered client " + client.peerstr
self.clients.remove(client)
def broadcast(self, msg):
print "broadcasting message '%s' .." % msg
for c in self.clients:
c.sendMessage(msg)
print "message sent to " + c.peerstr
class BroadcastPreparedServerFactory(BroadcastServerFactory):
"""
Functionally same as above, but optimized broadcast using
prepareMessage and sendPreparedMessage.
"""
def broadcast(self, msg):
print "broadcasting prepared message '%s' .." % msg
preparedMsg = self.prepareMessage(msg)
for c in self.clients:
c.sendPreparedMessage(preparedMsg)
print "prepared message sent to " + c.peerstr
def testing():
buffer2 - "hello"
myDisplay = process(buffer2)
myDisplay.start()
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == 'debug':
log.startLogging(sys.stdout)
debug = True
else:
debug = False
level_scheduler = Scheduler()
level_scheduler.add_interval_job(testing, seconds=5)
level_scheduler.start()
#ServerFactory = BroadcastServerFactory
ServerFactory = BroadcastPreparedServerFactory
factory = ServerFactory("ws://localhost:9000",
debug = debug,
debugCodePaths = debug)
factory.protocol = BroadcastServerProtocol
factory.setProtocolOptions(allowHixie76 = True)
listenWS(factory)
webdir = File(".")
web = Site(webdir)
reactor.listenTCP(8080, web)
reactor.run()
Thanks
Pass the class instance of BroadcastServerFactory to be called to the class instance that calls it process on creation
class process(threading.Thread):
def __init__(self, buffer3m, broadcast_server_factory):
threading.Thread.__init__(self)
self.setDaemon(True)
self.buffer3 = buffer3
self.factory = broadcast_server_factory
def run(self):
self.factory.broadcast("I don't know what I'm doing!")
and then call it (it's assigned as self.factory in the run statement. I can't see where you create a process class in your __main__ but it will be created with something like
p = process(buffer, factory)
Aside: Using capital letters for class names is considered good form in python process -> Process

Python - Twisted and PyAudio + Chat

I've been playing around with the Twisted extension and have fooled around with a chat room sort of system. However I want to expand upon it. As of now it only support multi-client chats with usernames ect. But I want to try and use the pyAudio extension to build a sort of VoIP application. I have a clientFactory and a simple echo protocol in the client and a serverFactory and protocol on the server. but I'm not entirely sure how to build off of this. What would be the best way to go about doing something like this?
Code to help if you need it
client.py:
import wx
from twisted.internet import wxreactor
wxreactor.install()
# import twisted reactor
import sys
from twisted.internet import reactor, protocol, task
from twisted.protocols import basic
class ChatFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent=None, title="WhiteNOISE")
self.protocol = None # twisted Protocol
sizer = wx.BoxSizer(wx.VERTICAL)
self.text = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY)
self.ctrl = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER, size=(300, 25))
sizer.Add(self.text, 5, wx.EXPAND)
sizer.Add(self.ctrl, 0, wx.EXPAND)
self.SetSizer(sizer)
self.ctrl.Bind(wx.EVT_TEXT_ENTER, self.send)
def send(self, evt):
self.protocol.sendLine(str(self.ctrl.GetValue()))
self.ctrl.SetValue("")
class DataForwardingProtocol(basic.LineReceiver):
def __init__(self):
self.output = None
def dataReceived(self, data):
gui = self.factory.gui
gui.protocol = self
if gui:
val = gui.text.GetValue()
gui.text.SetValue(val + data)
gui.text.SetInsertionPointEnd()
def connectionMade(self):
self.output = self.factory.gui.text # redirect Twisted's output
class ChatFactory(protocol.ClientFactory):
def __init__(self, gui):
self.gui = gui
self.protocol = DataForwardingProtocol
def clientConnectionLost(self, transport, reason):
reactor.stop()
def clientConnectionFailed(self, transport, reason):
reactor.stop()
if __name__ == '__main__':
app = wx.App(False)
frame = ChatFrame()
frame.Show()
reactor.registerWxApp(app)
reactor.connectTCP("192.168.1.115", 5001, ChatFactory(frame))
reactor.run()
server.py:
from twisted.internet import reactor, protocol
from twisted.protocols import basic
import time
def t():
return "["+ time.strftime("%H:%M:%S") +"] "
class EchoProtocol(basic.LineReceiver):
name = "Unnamed"
def connectionMade(self):
#on client connection made
self.sendLine("WhiteNOISE")
self.sendLine("Enter A Username Below...")
self.sendLine("")
self.count = 0
self.factory.clients.append(self)
print t() + "+ Connection from: "+ self.transport.getPeer().host
def connectionLost(self, reason):
#on client connection lost
self.sendMsg("- %s left." % self.name)
print t() + "- Connection lost: "+ self.name
self.factory.clients.remove(self)
def lineReceived(self, line):
#actions to do on message recive
if line == '/quit':
#close client connection
self.sendLine("Goodbye.")
self.transport.loseConnection()
return
elif line == "/userlist":
#send user list to single client, the one who requested it
self.chatters()
return
elif line.startswith("/me"):
#send an action formatted message
self.sendLine("**" + self.name + ": " + line.replace("/me",""))
return
elif line == "/?":
self.sendLine("Commands: /? /me /userlist /quit")
return
if not self.count:
self.username(line)
else:
self.sendMsg(self.name +": " + line)
def username(self, line):
#check if username already in use
for x in self.factory.clients:
if x.name == line:
self.sendLine("This username is taken; please choose another")
return
self.name = line
self.chatters()
self.sendLine("You have been connected!")
self.sendLine("")
self.count += 1
self.sendMsg("+ %s joined." % self.name)
print '%s~ %s connected as: %s' % (t(), self.transport.getPeer().host, self.name)
def chatters(self):
x = len(self.factory.clients) - 1
s = 'is' if x == 1 else 'are'
p = 'person' if x == 1 else 'people'
self.sendLine("There %s %i other %s connected:" % (s, x, p) )
for client in self.factory.clients:
if client is not self:
self.sendLine(client.name)
self.sendLine("")
def sendMsg(self, message):
#send message to all clients
for client in self.factory.clients:
client.sendLine(t() + message)
class EchoServerFactory(protocol.ServerFactory):
protocol = EchoProtocol
clients = []
if __name__ == "__main__":
reactor.listenTCP(5001, EchoServerFactory())
reactor.run()
Take a look at Divmod Sine, a SIP application server. The basic idea is that you need an additional network server in your application that will support the VoIP parts of the application.

twisted reactor receives no reply when running with trail

Here it is
There is class Body which sends the message to remote Perspective
Broker (PB) Friend.
The test is checking whether the Me instance can communicate with
Friend by calling *speak_ to _friend* or tell methods.
speak_ to_ friend is wrapper for Soul.speak virtual function, which is overwritten by Body.tell method.
Soul class is encapsulated into Body
tell method simulate blocking call by waiting for gotAnswer event from received callback function.
According to printouts, Friend communicates with Me in the same way for unittest and standalone cases.
The problem
... is that when running the unittest (unittest-me.py) with trial, the received callback is never called. Instead of that the silly error message printed on client side:
Connection was closed cleanly
For me it looks that reactor is closed before the deferred queue was completely processed.
If I am running standalone test test-body.py, then everything works as expected: Friend talks with me!
The questions
Why unittest behaves differently in this case?
How to fix it?
unittest output
Python2.7\Scripts\trial.py ..\Soapbox\Node\unittest-me.py
unittest-me
Trial_TestCase
test_conversation ... wake upConnecting to localhost:18800
DBG: speak
DBG: say
[FAIL]
Friend not willing to talk with me because: [Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionDone
'>: Connection was closed cleanly.
]
===============================================================================
[FAIL]
Traceback (most recent call last):
File "..\Soapbox\Node\unittest-me.py", line 50, in test_conversation
self.assertTrue(answer is not None, "Friend gave no answer")
twisted.trial.unittest.FailTest: Friend gave no answer
unittest-me.Trial_TestCase.test_conversation
-------------------------------------------------------------------------------
Ran 1 tests in 3.096s
FAILED (failures=1)
test-body output
python test-body.py
connector: <twisted.internet.tcp.Connector instance at 0x01C87940>
Press any key to continue...Starting reactor
wake upDBG: speak
DBG: say
DBG: received
Friend's reply: <username>
Press any key to continue...
That's all
freind's output
Dr. Livesey: I got your words: whoami
DBG: answer: <username>
Dr. Livesey: I got your words: whoami
DBG: answer: <username>
unittest-me.py
from twisted.trial import unittest
from twisted.spread import pb
from twisted.internet import reactor
from body import Body
class Trial_TestCase(unittest.TestCase):
def setUp(self):
self.ref = None
self.Me = Body('localhost', 18800)
self.assertTrue(self.Me is not None, "Creating Me assertion failed")
self.Me.start()
self.Me.pbFactory = pb.PBClientFactory(self.Me)
print "Connecting to " + self.Me.hostName + ":" + str(self.Me.portNo)
connector = reactor.connectTCP(self.Me.hostName,
self.Me.portNo,
self.Me.pbFactory)
self.addCleanup(connector.disconnect)
# =====================================================================
# unittest related parts
# =====================================================================
def gotRoot(ref):
self.ref = ref
df = self.Me.pbFactory.getRootObject()
'''
Without this dummy call, I can say no word - say not called
'''
obj = df.addCallback(gotRoot)
return obj
def tearDown(self):
self.Me.remoteObj.broker.transport.loseConnection()
self.Me.pbFactory.disconnect()
pass
def test_conversation(self):
# answer = self.Me.speak_to_friend()
answer = self.Me.tell(None)
self.assertTrue(answer is not None, "Friend gave no answer")
return self.Me.dm
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()
test-body.py
if __name__ == '__main__':
pass
from twisted.spread import pb
from twisted.internet import reactor, threads
import threading
import time
from body import Body
def controlThread(name, body):
print "Press any key to continue..."
raw_input()
body.start()
result = body.tell(None)
print "Friend's reply: " + str(result)
print "Press any key to continue..."
raw_input()
reactor.stop()
pass
body = Body('127.0.0.1', 18800)
f = pb.PBClientFactory(body)
body.pbFactory = f
connector = reactor.connectTCP("127.0.0.1", 18800, f)
print "connector: ", connector
threading.Thread(target=controlThread, args=("Control", body)).start()
print "Starting reactor"
reactor.run()
print "That's all"
body.py
import threading
import types
class Soul():
def speak(self):
'''
virtual method must be overwritten by derived class
'''
raise NotImplementedError("I cannot speak without a body. "\
"Please give me body first")
pass
class Body(threading.Thread):
def __init__(self, hostname, portno):
self.hostName = hostname
self.portNo = portno
self.df = None
self.dg = None
self.dm = None
self.remoteObj = None
self.pbFactory = None
self.answer = None
self.gotAnswer = threading.Event()
self.Soul = Soul()
self.Soul.speak = types.MethodType(self.tell, self.Soul)
threading.Thread.__init__(self)
pass
def run(self):
print "wake up"
pass
def speak_to_friend(self):
print 'DBG: speak_to_friend'
return self.Soul.speak()
def tell(self, soul):
print 'DBG: speak'
self.df = self.pbFactory.getRootObject()
self.answer = None
self.gotAnswer.clear()
self.dg = self.df.addCallback(self.say)
self.gotAnswer.wait(timeout=3)
return self.answer
def say(self, remoteObj):
print 'DBG: say'
self.remoteObj = remoteObj
self.dm = remoteObj.callRemote('talk', 'whoami')
self.dm.addCallback(self.received)
self.dm.addErrback(self.broken)
pass
def received(self, (answer, result)):
print 'DBG: received'
self.answer = '\n\t'.join(str.splitlines(answer))
self.gotAnswer.set()
pass
def broken(self,reason):
print "Friend not willing to talk with me because: ", reason.getErrorMessage()
pass
friend.py
from twisted.spread import pb
from twisted.internet import reactor
from twisted.internet import defer
from subprocess import Popen
import subprocess
from twisted.web import _newclient
class BrokenError(pb.Error): pass
class Friend(pb.Root):
def __init__(self,name):
self.counter = 0
self.name = str(name)
def remote_broken(self):
msg = "I don't hear you"
print "Talk louder" % msg
raise BrokenError(msg)
def broken(self,reason):
print "Communication is broken: ", reason.getErrorMessage()
# print "printError > %r" % failure
if reason.check(_newclient.RequestGenerationFailed):
print "printError: RequestGenerationFailed"
for f in reason.value.reasons:
print "printError > %r" % f
print f.getTraceback()
pass
def remote_talk(self,words):
print self.name + ": I got your words: " + words
process = Popen(words,stdout=subprocess.PIPE)
answer = process.stdout.read()
process.wait()
print "DBG: answer: ", answer
return (answer, process.returncode)
def main():
reactor.listenTCP(18800, pb.PBServerFactory(Friend("Dr. Livesey")))
reactor.run()
if __name__ == '__main__':
main()

Python Twisted receive command from TCP write to Serial device return response

I've managed to connect to usb modem and a client can connect via tcp to my reactor.listenTCP,the data received from modem will be send back to client. I'm want to take dataReceived from client and send this to modem..I'm struggling to get this to work.Any help will be highly appreciated! the code:
from twisted.internet import win32eventreactor
win32eventreactor.install()
from twisted.internet import reactor
from twisted.internet.serialport import SerialPort
from twisted.internet.protocol import Protocol, Factory
from twisted.python import log
import sys
log.startLogging(sys.stdout)
client_list = []#TCP clients connecting to me
class USBClient(Protocol):
def connectionFailed(self):
print "Connection Failed:", self
reactor.stop()
def connectionMade(self):
print 'Connected to USB modem'
USBClient.sendLine(self, 'AT\r\n')
def dataReceived(self, data):
print "Data received", repr(data)
print "Data received! with %d bytes!" % len(data)
#check & perhaps modify response and return to client
for cli in client_list:
cli.notifyClient(data)
pass
def lineReceived(self, line):
print "Line received", repr(line)
def sendLine(self, cmd):
print cmd
self.transport.write(cmd + "\r\n")
def outReceived(self, data):
print "outReceived! with %d bytes!" % len(data)
self.data = self.data + data
class CommandRx(Protocol):
def connectionMade(self):
print 'Connection received from tcp..'
client_list.append(self)
def dataReceived(self, data):
print 'Command receive', repr(data)
#Build command, if ok, send to serial port
#????
def connectionLost(self, reason):
print 'Connection lost', reason
if self in client_list:
print "Removing " + str(self)
client_list.remove(self)
def notifyClient(self, data):
self.transport.write(data)
class CommandRxFactory(Factory):
protocol = CommandRx
def __init__(self):
client_list = []
if __name__ == '__main__':
reactor.listenTCP(8000, CommandRxFactory())
SerialPort(USBClient(), 'COM8', reactor, baudrate='19200')
reactor.run()
Your problem is not about twisted, but about python. Read this FAQ entry:
How do I make input on one connection result in output on another?
Thing is, if you want to send stuff to a TCP-connected client in your serial-connected protocol, just pass to the protocol a reference to the factory, so you can use that reference to make the bridge.
Here's some example code that roughly does this:
class USBClient(Protocol):
def __init__(self, network):
self.network = network
def dataReceived(self, data):
print "Data received", repr(data)
#check & perhaps modify response and return to client
self.network.notifyAll(data)
#...
class CommandRx(Protocol):
def connectionMade(self):
self.factory.client_list.append(self)
def connectionLost(self, reason):
if self in self.factory.client_list:
self.factory.client_list.remove(self)
class CommandRxFactory(Factory):
protocol = CommandRx
def __init__(self):
self.client_list = []
def notifyAll(self, data):
for cli in self.client_list:
cli.transport.write(data)
When initializing, pass the reference:
tcpfactory = CommandRxFactory()
reactor.listenTCP(8000, tcpfactory)
SerialPort(USBClient(tcpfactory), 'COM8', reactor, baudrate='19200')
reactor.run()
Thanks, got it to work.Could not get def notifyAll to send data to my modem..did it like this:for cli in client_list: cli.transport.write(data)
new code:
import sys
from twisted.internet import reactor
from twisted.internet.protocol import Factory
from twisted.internet.protocol import Protocol
from twisted.internet.serialport import SerialPort
from twisted.python import log
log.startLogging(sys.stdout)
client_list = []#TCP clients connecting to me
usb_list = []
class USBClient(Protocol):
def __init__(self, network):
self.network = network
self.usb_list = []
def connectionFailed(self):
print "Connection Failed:", self
reactor.stop()
def connectionMade(self):
usb_list.append(self)
print 'Connected to USB modem'
USBClient.sendLine(self, 'AT\r\n')
def dataReceived(self, data):
print "Data received", repr(data)
print "Data received! with %d bytes!" % len(data)
for cli in client_list:
cli.transport.write(data)
#self.network.notifyAll(data)# !!AArgh..!Could not get this to work
pass
def lineReceived(self, line):
print "Line received", repr(line)
def sendLine(self, cmd):
print cmd
self.transport.write(cmd + "\r\n")
def outReceived(self, data):
print "outReceived! with %d bytes!" % len(data)
self.data = self.data + data
class CommandRx(Protocol):
def connectionMade(self):
print 'Connection received from tcp..'
client_list.append(self)
def dataReceived(self, data):
print 'Command receive', repr(data)
for usb in usb_list:
usb.transport.write(data)
def connectionLost(self, reason):
print 'Connection lost', reason
if self in client_list:
print "Removing " + str(self)
client_list.remove(self)
class CommandRxFactory(Factory):
protocol = CommandRx
def __init__(self):
self.client_list = []
def notifyAll(self, data):
for cli in self.client_list:
cli.transport.write('yipee')
if __name__ == '__main__':
tcpfactory = CommandRxFactory()
reactor.listenTCP(8000, tcpfactory)
SerialPort(USBClient(tcpfactory), '/dev/ttyUSB4', reactor, baudrate='19200')
reactor.run()

Categories

Resources