mirror of
https://github.com/pybricks/pybricks-projects.git
synced 2026-09-15 11:03:15 +00:00
This restores the previously deleted custom StreamRequestHandler. In the prevous commits, more and more pieces from the standard socket library were used instead of the custom-made ones made for EV3. This was giving compatibility issues on some platforms (Windows/Mac), and differences with EV3 made it harder to debug. It was previously dropped to make the MailboxHandler work. This commit reverts that change and modifies the MailboxHandler instead to make it work with the BluetoothSocket objects, which do not have rfile.read. Instead we use recv directly and rely on zero length to detect disconnected state. It would be useful to eventually use only components from the socket server module and no incomplete custom ones, but this provides at least a working cross platform starting point to fall back to.
146 lines
3.8 KiB
Python
146 lines
3.8 KiB
Python
# SPDX-License-Identifier: MIT
|
|
# Copyright (C) 2020 The Pybricks Authors
|
|
|
|
"""
|
|
:class:`RFCOMMServer` can be used to communicate with other Bluetooth RFCOMM
|
|
devices that don't support the EV3 mailbox protocol.
|
|
|
|
It is based on the standard library ``socketserver`` module and attempts to
|
|
remain a strict subset of that implementation when it comes to low-level
|
|
implementation details.
|
|
"""
|
|
|
|
from bluetooth import BluetoothSocket, RFCOMM
|
|
from socketserver import ThreadingMixIn
|
|
|
|
BDADDR_ANY = ""
|
|
|
|
|
|
def str2ba(string, ba):
|
|
"""Convert string to Bluetooth address"""
|
|
for i, v in enumerate(string.split(':')):
|
|
ba.b[5-i] = int(v, 16)
|
|
|
|
|
|
def ba2str(ba):
|
|
"""Convert Bluetooth address to string"""
|
|
string = []
|
|
for b in ba.b:
|
|
string.append('{:02X}'.format(b))
|
|
string.reverse()
|
|
return ':'.join(string).upper()
|
|
|
|
|
|
class RFCOMMServer:
|
|
"""Object that simplifies setting up an RFCOMM socket server.
|
|
|
|
This is based on the ``socketserver.SocketServer`` class in the Python
|
|
standard library.
|
|
"""
|
|
request_queue_size = 1
|
|
|
|
def __init__(self, server_address, RequestHandlerClass):
|
|
self.server_address = server_address
|
|
self.RequestHandlerClass = RequestHandlerClass
|
|
|
|
self.socket = BluetoothSocket(RFCOMM)
|
|
|
|
try:
|
|
self.socket.bind((server_address[0], server_address[1]))
|
|
# self.server_address = self.socket.getsockname()
|
|
self.socket.listen(self.request_queue_size)
|
|
except Exception:
|
|
self.server_close()
|
|
raise
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, type, value, traceback):
|
|
self.server_close()
|
|
|
|
def handle_request(self):
|
|
try:
|
|
request, addr_data = self.socket.accept()
|
|
except OSError:
|
|
return
|
|
|
|
try:
|
|
self.process_request(request, addr_data)
|
|
except Exception:
|
|
request.close()
|
|
raise
|
|
|
|
def process_request(self, request, client_address):
|
|
self.finish_request(request, client_address)
|
|
request.close()
|
|
|
|
def finish_request(self, request, client_address):
|
|
self.RequestHandlerClass(request, client_address, self)
|
|
|
|
def server_close(self):
|
|
self.socket.close()
|
|
|
|
|
|
class StreamRequestHandler:
|
|
"""Class that handles incoming requests.
|
|
|
|
This is based on ``socketserver.StreamRequestHandler`` from the Python
|
|
standard library.
|
|
"""
|
|
def __init__(self, request, client_address, server):
|
|
self.request = request
|
|
self.client_address = client_address
|
|
self.server = server
|
|
self.setup()
|
|
try:
|
|
self.handle()
|
|
finally:
|
|
self.finish()
|
|
|
|
def setup(self):
|
|
self.wfile = self.request
|
|
self.rfile = self.request
|
|
|
|
def handle(self):
|
|
pass
|
|
|
|
def finish(self):
|
|
pass
|
|
|
|
|
|
class ThreadingRFCOMMServer(ThreadingMixIn, RFCOMMServer):
|
|
"""Version of :class:`RFCOMMServer` that handles connections in a new
|
|
thread.
|
|
"""
|
|
pass
|
|
|
|
|
|
class RFCOMMClient:
|
|
def __init__(self, client_address, RequestHandlerClass):
|
|
self.client_address = client_address
|
|
self.RequestHandlerClass = RequestHandlerClass
|
|
self.socket = BluetoothSocket(RFCOMM)
|
|
|
|
def handle_request(self):
|
|
self.socket.connect((self.client_address[0], self.client_address[1]))
|
|
try:
|
|
self.process_request(self.socket, self.client_address)
|
|
except Exception:
|
|
self.socket.close()
|
|
raise
|
|
|
|
def process_request(self, request, client_address):
|
|
self.finish_request(request, client_address)
|
|
request.close()
|
|
|
|
def finish_request(self, request, client_address):
|
|
self.RequestHandlerClass(request, client_address, self)
|
|
|
|
def client_close(self):
|
|
self.socket.close()
|
|
|
|
|
|
class ThreadingRFCOMMClient(ThreadingMixIn, RFCOMMClient):
|
|
pass
|