cleanup types
This commit is contained in:
parent
841da23c47
commit
b6203b611a
6
pyproject.toml
Normal file
6
pyproject.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[tool.ruff]
|
||||||
|
line-length = 88
|
||||||
|
|
||||||
|
[tool.pyright]
|
||||||
|
typeCheckingMode = "strict"
|
||||||
|
reportMissingTypeStubs = "warning"
|
||||||
17
src/aoa.py
Normal file
17
src/aoa.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import numpy as np
|
||||||
|
import numpy.typing as npt
|
||||||
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
class AoA:
|
||||||
|
def __init__(self):
|
||||||
|
self.historical_data = np.array([])
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update(self, data: npt.NDArray[np.complex128]):
|
||||||
|
self.historical_data = np.concatenate([self.historical_data, data], axis=0)
|
||||||
|
if self.historical_data.shape[0] > config.AOA_SLIDING_WINDOW_SIZE:
|
||||||
|
self.historical_data = self.historical_data[
|
||||||
|
-config.AOA_SLIDING_WINDOW_SIZE :
|
||||||
|
]
|
||||||
|
pass
|
||||||
8
src/config.py
Normal file
8
src/config.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
PREPROCESSING_SHORT_TERM_WINDOW_SIZE = 5
|
||||||
|
PREPROCESSING_LONG_TERM_ALPHA = 0.1
|
||||||
|
AOA_SLIDING_WINDOW_SIZE = 20
|
||||||
|
|
||||||
|
FEITCSI_IP_ADDRESS = os.getenv("IP_ADDRESS", "10.0.12.62")
|
||||||
|
FEITCSI_PORT = 8008
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import struct
|
import struct
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
|
|
||||||
RATE_MCS_MOD_TYPE_POS = 8
|
RATE_MCS_MOD_TYPE_POS = 8
|
||||||
@ -93,13 +94,13 @@ class CSIHeader:
|
|||||||
class CSI:
|
class CSI:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parseCsiData(data: bytes, header: CSIHeader):
|
def parseCsiData(data: bytes, header: CSIHeader):
|
||||||
csi_matrix = np.zeros(
|
csi_matrix: npt.NDArray[np.complex128] = np.zeros(
|
||||||
(
|
(
|
||||||
header.num_subcarriers,
|
header.num_subcarriers,
|
||||||
header.num_rx,
|
header.num_rx,
|
||||||
header.num_tx,
|
header.num_tx,
|
||||||
),
|
),
|
||||||
dtype=complex,
|
dtype=np.complex128,
|
||||||
)
|
)
|
||||||
pos = 0
|
pos = 0
|
||||||
for j in range(header.num_rx):
|
for j in range(header.num_rx):
|
||||||
|
|||||||
16
src/main.py
16
src/main.py
@ -1,10 +1,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
|
from typing import Callable
|
||||||
from csi import CSI
|
from csi import CSI
|
||||||
|
from aoa import AoA
|
||||||
from preprocess import Preprocessor
|
from preprocess import Preprocessor
|
||||||
import visualise
|
import visualise
|
||||||
import threading
|
import threading
|
||||||
import struct
|
import struct
|
||||||
|
import config
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
@ -21,7 +24,7 @@ class FeitServer:
|
|||||||
frame_format: str = "VHT",
|
frame_format: str = "VHT",
|
||||||
):
|
):
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.port = 8008
|
self.port = config.FEITCSI_PORT
|
||||||
self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
self.server.connect((self.ip, self.port))
|
self.server.connect((self.ip, self.port))
|
||||||
self.start_string = (
|
self.start_string = (
|
||||||
@ -32,8 +35,11 @@ class FeitServer:
|
|||||||
self.server.send(b"stop\n")
|
self.server.send(b"stop\n")
|
||||||
self.server.send(self.start_string.encode())
|
self.server.send(self.start_string.encode())
|
||||||
|
|
||||||
def listen(self, callback):
|
def listen(self, callback: Callable[[CSI], None]):
|
||||||
while True:
|
while True:
|
||||||
|
# This is the max size of a UDP packet. The size of the actual CSI
|
||||||
|
# packet will depend on the frame format and channel width, which
|
||||||
|
# changes the number of subcarriers
|
||||||
data = self.server.recv(65535)
|
data = self.server.recv(65535)
|
||||||
try:
|
try:
|
||||||
csidata = CSI(data)
|
csidata = CSI(data)
|
||||||
@ -42,15 +48,17 @@ class FeitServer:
|
|||||||
logging.error("Failed to parse CSI data")
|
logging.error("Failed to parse CSI data")
|
||||||
|
|
||||||
|
|
||||||
def process_data(data):
|
def process_data(data: CSI):
|
||||||
logging.debug("Got CSI frame")
|
logging.debug("Got CSI frame")
|
||||||
processed = preprocess.preprocess(data)
|
processed = preprocess.preprocess(data)
|
||||||
visualise.add_data(processed)
|
visualise.add_data(processed)
|
||||||
|
aoa.update(processed)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
server = FeitServer("10.0.12.62")
|
server = FeitServer(config.FEITCSI_IP_ADDRESS)
|
||||||
preprocess = Preprocessor()
|
preprocess = Preprocessor()
|
||||||
|
aoa = AoA()
|
||||||
|
|
||||||
# Start webapp in background thread
|
# Start webapp in background thread
|
||||||
webapp = threading.Thread(target=visualise.start)
|
webapp = threading.Thread(target=visualise.start)
|
||||||
|
|||||||
@ -2,19 +2,18 @@ import numpy as np
|
|||||||
from csi import CSI
|
from csi import CSI
|
||||||
import logging
|
import logging
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
|
import config
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
class Preprocessor:
|
class Preprocessor:
|
||||||
NUM_SHORT_TERM_ENTRIES = 10
|
|
||||||
LONG_TERM_ALPHA = 0.1
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.prev_entries = Queue(maxsize=100)
|
self.prev_entries: Queue[npt.NDArray[np.complex128]] = Queue(maxsize=100)
|
||||||
self.short_term_avg = np.zeros((1,), dtype=complex)
|
self.short_term_avg = np.zeros((1,), dtype=np.complex128)
|
||||||
self.long_term_avg = np.zeros((1,), dtype=complex)
|
self.long_term_avg = np.zeros((1,), dtype=np.complex128)
|
||||||
|
|
||||||
def preprocess(self, csi: CSI):
|
def preprocess(self, csi: CSI):
|
||||||
h = csi.matrix
|
h = csi.matrix
|
||||||
@ -22,18 +21,20 @@ class Preprocessor:
|
|||||||
|
|
||||||
# Assume that all csi matrices will have the same shape
|
# Assume that all csi matrices will have the same shape
|
||||||
if self.short_term_avg.shape != h_hat.shape:
|
if self.short_term_avg.shape != h_hat.shape:
|
||||||
self.short_term_avg = np.zeros(h_hat.shape, dtype=complex)
|
self.short_term_avg = np.zeros(h_hat.shape, dtype=np.complex128)
|
||||||
self.long_term_avg = np.zeros(h_hat.shape, dtype=complex)
|
self.long_term_avg = np.zeros(h_hat.shape, dtype=np.complex128)
|
||||||
self.prev_entries = Queue(maxsize=100)
|
self.prev_entries = Queue(maxsize=100)
|
||||||
|
|
||||||
while self.prev_entries.full():
|
while self.prev_entries.full():
|
||||||
old_value = self.prev_entries.get()
|
old_value = self.prev_entries.get()
|
||||||
self.short_term_avg -= old_value / self.NUM_SHORT_TERM_ENTRIES
|
self.short_term_avg -= (
|
||||||
|
old_value / config.PREPROCESSING_SHORT_TERM_WINDOW_SIZE
|
||||||
|
)
|
||||||
self.prev_entries.put(h_hat)
|
self.prev_entries.put(h_hat)
|
||||||
self.short_term_avg += h_hat / self.NUM_SHORT_TERM_ENTRIES
|
self.short_term_avg += h_hat / config.PREPROCESSING_SHORT_TERM_WINDOW_SIZE
|
||||||
self.long_term_avg = (
|
self.long_term_avg = (
|
||||||
self.long_term_avg * (1 - self.LONG_TERM_ALPHA)
|
self.long_term_avg * (1 - config.PREPROCESSING_LONG_TERM_ALPHA)
|
||||||
+ self.short_term_avg * self.LONG_TERM_ALPHA
|
+ self.short_term_avg * config.PREPROCESSING_LONG_TERM_ALPHA
|
||||||
)
|
)
|
||||||
|
|
||||||
# Remove static components
|
# Remove static components
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
from flask import Flask, render_template, request
|
from flask import Flask, render_template
|
||||||
from flask_sock import Sock
|
from flask_sock import Sock
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
import json
|
from simple_websocket import Server
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
sock = Sock(app)
|
sock = Sock(app)
|
||||||
@ -16,17 +16,21 @@ def preprocessed():
|
|||||||
return render_template("preprocessed.html")
|
return render_template("preprocessed.html")
|
||||||
|
|
||||||
|
|
||||||
subscribers = {}
|
type Subscriber = Server
|
||||||
|
subscriber_settings: dict[Subscriber, tuple[int, int, int]] = {}
|
||||||
|
|
||||||
|
|
||||||
@sock.route("/data")
|
@sock.route("/data")
|
||||||
def get_data(sock):
|
def get_data(sock: Subscriber):
|
||||||
while True:
|
while True:
|
||||||
msg = sock.receive()
|
msg = sock.receive()
|
||||||
subscribers[sock] = list(map(int, msg.split()))
|
if len(msg.split()) != 3:
|
||||||
|
break
|
||||||
|
subcarrier, rx, tx = map(int, msg.split())
|
||||||
|
subscriber_settings[sock] = (subcarrier, rx, tx)
|
||||||
|
|
||||||
|
|
||||||
def add_data(new_data):
|
def add_data(new_data: npt.NDArray[np.complex128]):
|
||||||
global data
|
global data
|
||||||
if data.size == 0:
|
if data.size == 0:
|
||||||
data = np.expand_dims(new_data, axis=0)
|
data = np.expand_dims(new_data, axis=0)
|
||||||
@ -36,17 +40,17 @@ def add_data(new_data):
|
|||||||
# Only keep latest 100 entries
|
# Only keep latest 100 entries
|
||||||
if data.shape[0] > 100:
|
if data.shape[0] > 100:
|
||||||
data = data[-100:]
|
data = data[-100:]
|
||||||
to_remove = []
|
to_remove: list[Subscriber] = []
|
||||||
for subscriber in subscribers:
|
for subscriber in subscriber_settings:
|
||||||
try:
|
try:
|
||||||
subcarrier, rx, tx = subscribers[subscriber]
|
subcarrier, rx, tx = subscriber_settings[subscriber]
|
||||||
subscriber.send(new_data.real[subcarrier, rx, tx])
|
subscriber.send(new_data.real[subcarrier, rx, tx])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
to_remove.append(subscriber)
|
to_remove.append(subscriber)
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
for subscriber in to_remove:
|
for subscriber in to_remove:
|
||||||
del subscribers[subscriber]
|
del subscriber_settings[subscriber]
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
|
|||||||
@ -12,7 +12,7 @@ Plotly.newPlot(
|
|||||||
{
|
{
|
||||||
x: [],
|
x: [],
|
||||||
y: [],
|
y: [],
|
||||||
type: "scatter", // Line chart
|
type: "scatter",
|
||||||
mode: "lines+markers", // Line + markers
|
mode: "lines+markers", // Line + markers
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
56
typings/flask_sock/__init__.pyi
Normal file
56
typings/flask_sock/__init__.pyi
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from flask import Blueprint, Response, current_app, request
|
||||||
|
from simple_websocket import ConnectionClosed, Server
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
class Sock:
|
||||||
|
"""Instantiate the Flask-Sock extension.
|
||||||
|
|
||||||
|
:param app: The Flask application instance. If not provided, it must be
|
||||||
|
initialized later by calling the :func:`Sock.init_app` method.
|
||||||
|
"""
|
||||||
|
def __init__(self, app=...) -> None: ...
|
||||||
|
def init_app(self, app): # -> None:
|
||||||
|
"""Initialize the Flask-Socket extension.
|
||||||
|
|
||||||
|
|
||||||
|
:param app: The Flask application instance. This method only needs to
|
||||||
|
be called if the application instance was not passed as
|
||||||
|
an argument to the constructor.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def route(
|
||||||
|
self, path: str, bp: Blueprint | None = None
|
||||||
|
) -> Callable[
|
||||||
|
[Callable[[Server], None]], Callable[[Server], None]
|
||||||
|
]: # -> Callable[..., None]:
|
||||||
|
"""Decorator to create a WebSocket route.
|
||||||
|
|
||||||
|
The decorated function will be invoked when a WebSocket client
|
||||||
|
establishes a connection, with a WebSocket connection object passed
|
||||||
|
as an argument. Example::
|
||||||
|
|
||||||
|
@sock.route('/ws')
|
||||||
|
def websocket_route(ws):
|
||||||
|
# The ws object has the following methods:
|
||||||
|
# - ws.send(data)
|
||||||
|
# - ws.receive(timeout=None)
|
||||||
|
# - ws.close(reason=None, message=None)
|
||||||
|
|
||||||
|
If the route has variable components, the ``ws`` argument needs to be
|
||||||
|
included before them.
|
||||||
|
|
||||||
|
:param path: the URL associated with the route.
|
||||||
|
:param bp: the blueprint on which to register the route. If not given,
|
||||||
|
the route is attached directly to the Flask application
|
||||||
|
instance. When a blueprint is used, the application is
|
||||||
|
responsible for the blueprint's registration.
|
||||||
|
:param kwargs: additional route options. See the Flask documentation
|
||||||
|
for the ``app.route`` decorator for details.
|
||||||
|
"""
|
||||||
|
...
|
||||||
16
typings/simple_websocket/__init__.pyi
Normal file
16
typings/simple_websocket/__init__.pyi
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .ws import Client, Server
|
||||||
|
from .aiows import AioClient, AioServer
|
||||||
|
from .errors import ConnectionClosed, ConnectionError
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"Client",
|
||||||
|
"Server",
|
||||||
|
"AioClient",
|
||||||
|
"AioServer",
|
||||||
|
"ConnectionClosed",
|
||||||
|
"ConnectionError",
|
||||||
|
]
|
||||||
163
typings/simple_websocket/aiows.pyi
Normal file
163
typings/simple_websocket/aiows.pyi
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class AioBase:
|
||||||
|
def __init__(self, connection_type=..., receive_bytes=..., ping_interval=..., max_message_size=...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def connect(self): # -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def handshake(self): # -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def send(self, data): # -> None:
|
||||||
|
"""Send data over the WebSocket connection.
|
||||||
|
|
||||||
|
:param data: The data to send. If ``data`` is of type ``bytes``, then
|
||||||
|
a binary message is sent. Else, the message is sent in
|
||||||
|
text format.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
async def receive(self, timeout=...): # -> None:
|
||||||
|
"""Receive data over the WebSocket connection.
|
||||||
|
|
||||||
|
:param timeout: Amount of time to wait for the data, in seconds. Set
|
||||||
|
to ``None`` (the default) to wait indefinitely. Set
|
||||||
|
to 0 to read without blocking.
|
||||||
|
|
||||||
|
The data received is returned, as ``bytes`` or ``str``, depending on
|
||||||
|
the type of the incoming message.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
async def close(self, reason=..., message=...): # -> None:
|
||||||
|
"""Close the WebSocket connection.
|
||||||
|
|
||||||
|
:param reason: A numeric status code indicating the reason of the
|
||||||
|
closure, as defined by the WebSocket specification. The
|
||||||
|
default is 1000 (normal closure).
|
||||||
|
:param message: A text message to be sent to the other side.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def choose_subprotocol(self, request): # -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AioServer(AioBase):
|
||||||
|
"""This class implements a WebSocket server.
|
||||||
|
|
||||||
|
Instead of creating an instance of this class directly, use the
|
||||||
|
``accept()`` class method to create individual instances of the server,
|
||||||
|
each bound to a client request.
|
||||||
|
"""
|
||||||
|
def __init__(self, request, subprotocols=..., receive_bytes=..., ping_interval=..., max_message_size=...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def accept(cls, aiohttp=..., asgi=..., sock=..., headers=..., subprotocols=..., receive_bytes=..., ping_interval=..., max_message_size=...): # -> WebSocketASGI | Self:
|
||||||
|
"""Accept a WebSocket connection from a client.
|
||||||
|
|
||||||
|
:param aiohttp: The request object from aiohttp. If this argument is
|
||||||
|
provided, ``asgi``, ``sock`` and ``headers`` must not
|
||||||
|
be set.
|
||||||
|
:param asgi: A (scope, receive, send) tuple from an ASGI request. If
|
||||||
|
this argument is provided, ``aiohttp``, ``sock`` and
|
||||||
|
``headers`` must not be set.
|
||||||
|
:param sock: A connected socket to use. If this argument is provided,
|
||||||
|
``aiohttp`` and ``asgi`` must not be set. The ``headers``
|
||||||
|
argument must be set with the incoming request headers.
|
||||||
|
:param headers: A dictionary with the incoming request headers, when
|
||||||
|
``sock`` is used.
|
||||||
|
:param subprotocols: A list of supported subprotocols, or ``None`` (the
|
||||||
|
default) to disable subprotocol negotiation.
|
||||||
|
:param receive_bytes: The size of the receive buffer, in bytes. The
|
||||||
|
default is 4096.
|
||||||
|
:param ping_interval: Send ping packets to clients at the requested
|
||||||
|
interval in seconds. Set to ``None`` (the
|
||||||
|
default) to disable ping/pong logic. Enable to
|
||||||
|
prevent disconnections when the line is idle for
|
||||||
|
a certain amount of time, or to detect
|
||||||
|
unresponsive clients and disconnect them. A
|
||||||
|
recommended interval is 25 seconds.
|
||||||
|
:param max_message_size: The maximum size allowed for a message, in
|
||||||
|
bytes, or ``None`` for no limit. The default
|
||||||
|
is ``None``.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
async def handshake(self): # -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def choose_subprotocol(self, request): # -> None:
|
||||||
|
"""Choose a subprotocol to use for the WebSocket connection.
|
||||||
|
|
||||||
|
The default implementation selects the first protocol requested by the
|
||||||
|
client that is accepted by the server. Subclasses can override this
|
||||||
|
method to implement a different subprotocol negotiation algorithm.
|
||||||
|
|
||||||
|
:param request: A ``Request`` object.
|
||||||
|
|
||||||
|
The method should return the subprotocol to use, or ``None`` if no
|
||||||
|
subprotocol is chosen.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AioClient(AioBase):
|
||||||
|
"""This class implements a WebSocket client.
|
||||||
|
|
||||||
|
Instead of creating an instance of this class directly, use the
|
||||||
|
``connect()`` class method to create an instance that is connected to a
|
||||||
|
server.
|
||||||
|
"""
|
||||||
|
def __init__(self, url, subprotocols=..., headers=..., receive_bytes=..., ping_interval=..., max_message_size=..., ssl_context=...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def connect(cls, url, subprotocols=..., headers=..., receive_bytes=..., ping_interval=..., max_message_size=..., ssl_context=..., thread_class=..., event_class=...): # -> Self:
|
||||||
|
"""Returns a WebSocket client connection.
|
||||||
|
|
||||||
|
:param url: The connection URL. Both ``ws://`` and ``wss://`` URLs are
|
||||||
|
accepted.
|
||||||
|
:param subprotocols: The name of the subprotocol to use, or a list of
|
||||||
|
subprotocol names in order of preference. Set to
|
||||||
|
``None`` (the default) to not use a subprotocol.
|
||||||
|
:param headers: A dictionary or list of tuples with additional HTTP
|
||||||
|
headers to send with the connection request. Note that
|
||||||
|
custom headers are not supported by the WebSocket
|
||||||
|
protocol, so the use of this parameter is not
|
||||||
|
recommended.
|
||||||
|
:param receive_bytes: The size of the receive buffer, in bytes. The
|
||||||
|
default is 4096.
|
||||||
|
:param ping_interval: Send ping packets to the server at the requested
|
||||||
|
interval in seconds. Set to ``None`` (the
|
||||||
|
default) to disable ping/pong logic. Enable to
|
||||||
|
prevent disconnections when the line is idle for
|
||||||
|
a certain amount of time, or to detect an
|
||||||
|
unresponsive server and disconnect. A recommended
|
||||||
|
interval is 25 seconds. In general it is
|
||||||
|
preferred to enable ping/pong on the server, and
|
||||||
|
let the client respond with pong (which it does
|
||||||
|
regardless of this setting).
|
||||||
|
:param max_message_size: The maximum size allowed for a message, in
|
||||||
|
bytes, or ``None`` for no limit. The default
|
||||||
|
is ``None``.
|
||||||
|
:param ssl_context: An ``SSLContext`` instance, if a default SSL
|
||||||
|
context isn't sufficient.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
async def handshake(self): # -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def close(self, reason=..., message=...): # -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
23
typings/simple_websocket/asgi.pyi
Normal file
23
typings/simple_websocket/asgi.pyi
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class WebSocketASGI:
|
||||||
|
def __init__(self, scope, receive, send, subprotocols=...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def accept(cls, scope, receive, send, subprotocols=...): # -> WebSocketASGI:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def receive(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
async def send(self, data): # -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def close(self): # -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
22
typings/simple_websocket/errors.pyi
Normal file
22
typings/simple_websocket/errors.pyi
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class SimpleWebsocketError(RuntimeError):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionError(SimpleWebsocketError):
|
||||||
|
"""Connection error exception class."""
|
||||||
|
def __init__(self, status_code=...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionClosed(SimpleWebsocketError):
|
||||||
|
"""Connection closed exception class."""
|
||||||
|
def __init__(self, reason=..., message=...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
211
typings/simple_websocket/ws.pyi
Normal file
211
typings/simple_websocket/ws.pyi
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Base:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
sock=...,
|
||||||
|
connection_type=...,
|
||||||
|
receive_bytes=...,
|
||||||
|
ping_interval=...,
|
||||||
|
max_message_size=...,
|
||||||
|
thread_class=...,
|
||||||
|
event_class=...,
|
||||||
|
selector_class=...,
|
||||||
|
) -> None: ...
|
||||||
|
def handshake(self): # -> None:
|
||||||
|
...
|
||||||
|
def send(self, data: bytes | str) -> None: # -> None:
|
||||||
|
"""Send data over the WebSocket connection.
|
||||||
|
|
||||||
|
:param data: The data to send. If ``data`` is of type ``bytes``, then
|
||||||
|
a binary message is sent. Else, the message is sent in
|
||||||
|
text format.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def receive(self, timeout: float | None = ...) -> bytes | str: # -> None:
|
||||||
|
"""Receive data over the WebSocket connection.
|
||||||
|
|
||||||
|
:param timeout: Amount of time to wait for the data, in seconds. Set
|
||||||
|
to ``None`` (the default) to wait indefinitely. Set
|
||||||
|
to 0 to read without blocking.
|
||||||
|
|
||||||
|
The data received is returned, as ``bytes`` or ``str``, depending on
|
||||||
|
the type of the incoming message.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def close(self, reason=..., message=...): # -> None:
|
||||||
|
"""Close the WebSocket connection.
|
||||||
|
|
||||||
|
:param reason: A numeric status code indicating the reason of the
|
||||||
|
closure, as defined by the WebSocket specification. The
|
||||||
|
default is 1000 (normal closure).
|
||||||
|
:param message: A text message to be sent to the other side.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def choose_subprotocol(self, request): # -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
class Server(Base):
|
||||||
|
"""This class implements a WebSocket server.
|
||||||
|
|
||||||
|
Instead of creating an instance of this class directly, use the
|
||||||
|
``accept()`` class method to create individual instances of the server,
|
||||||
|
each bound to a client request.
|
||||||
|
"""
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
environ,
|
||||||
|
subprotocols=...,
|
||||||
|
receive_bytes=...,
|
||||||
|
ping_interval=...,
|
||||||
|
max_message_size=...,
|
||||||
|
thread_class=...,
|
||||||
|
event_class=...,
|
||||||
|
selector_class=...,
|
||||||
|
) -> None: ...
|
||||||
|
@classmethod
|
||||||
|
def accept(
|
||||||
|
cls,
|
||||||
|
environ,
|
||||||
|
subprotocols=...,
|
||||||
|
receive_bytes=...,
|
||||||
|
ping_interval=...,
|
||||||
|
max_message_size=...,
|
||||||
|
thread_class=...,
|
||||||
|
event_class=...,
|
||||||
|
selector_class=...,
|
||||||
|
): # -> Self:
|
||||||
|
"""Accept a WebSocket connection from a client.
|
||||||
|
|
||||||
|
:param environ: A WSGI ``environ`` dictionary with the request details.
|
||||||
|
Among other things, this class expects to find the
|
||||||
|
low-level network socket for the connection somewhere
|
||||||
|
in this dictionary. Since the WSGI specification does
|
||||||
|
not cover where or how to store this socket, each web
|
||||||
|
server does this in its own different way. Werkzeug,
|
||||||
|
Gunicorn, Eventlet and Gevent are the only web servers
|
||||||
|
that are currently supported.
|
||||||
|
:param subprotocols: A list of supported subprotocols, or ``None`` (the
|
||||||
|
default) to disable subprotocol negotiation.
|
||||||
|
:param receive_bytes: The size of the receive buffer, in bytes. The
|
||||||
|
default is 4096.
|
||||||
|
:param ping_interval: Send ping packets to clients at the requested
|
||||||
|
interval in seconds. Set to ``None`` (the
|
||||||
|
default) to disable ping/pong logic. Enable to
|
||||||
|
prevent disconnections when the line is idle for
|
||||||
|
a certain amount of time, or to detect
|
||||||
|
unresponsive clients and disconnect them. A
|
||||||
|
recommended interval is 25 seconds.
|
||||||
|
:param max_message_size: The maximum size allowed for a message, in
|
||||||
|
bytes, or ``None`` for no limit. The default
|
||||||
|
is ``None``.
|
||||||
|
:param thread_class: The ``Thread`` class to use when creating
|
||||||
|
background threads. The default is the
|
||||||
|
``threading.Thread`` class from the Python
|
||||||
|
standard library.
|
||||||
|
:param event_class: The ``Event`` class to use when creating event
|
||||||
|
objects. The default is the `threading.Event``
|
||||||
|
class from the Python standard library.
|
||||||
|
:param selector_class: The ``Selector`` class to use when creating
|
||||||
|
selectors. The default is the
|
||||||
|
``selectors.DefaultSelector`` class from the
|
||||||
|
Python standard library.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def handshake(self): # -> None:
|
||||||
|
...
|
||||||
|
def choose_subprotocol(self, request): # -> None:
|
||||||
|
"""Choose a subprotocol to use for the WebSocket connection.
|
||||||
|
|
||||||
|
The default implementation selects the first protocol requested by the
|
||||||
|
client that is accepted by the server. Subclasses can override this
|
||||||
|
method to implement a different subprotocol negotiation algorithm.
|
||||||
|
|
||||||
|
:param request: A ``Request`` object.
|
||||||
|
|
||||||
|
The method should return the subprotocol to use, or ``None`` if no
|
||||||
|
subprotocol is chosen.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
class Client(Base):
|
||||||
|
"""This class implements a WebSocket client.
|
||||||
|
|
||||||
|
Instead of creating an instance of this class directly, use the
|
||||||
|
``connect()`` class method to create an instance that is connected to a
|
||||||
|
server.
|
||||||
|
"""
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
url,
|
||||||
|
subprotocols=...,
|
||||||
|
headers=...,
|
||||||
|
receive_bytes=...,
|
||||||
|
ping_interval=...,
|
||||||
|
max_message_size=...,
|
||||||
|
ssl_context=...,
|
||||||
|
thread_class=...,
|
||||||
|
event_class=...,
|
||||||
|
) -> None: ...
|
||||||
|
@classmethod
|
||||||
|
def connect(
|
||||||
|
cls,
|
||||||
|
url: str,
|
||||||
|
subprotocols=...,
|
||||||
|
headers=...,
|
||||||
|
receive_bytes=...,
|
||||||
|
ping_interval=...,
|
||||||
|
max_message_size=...,
|
||||||
|
ssl_context=...,
|
||||||
|
thread_class=...,
|
||||||
|
event_class=...,
|
||||||
|
): # -> Self:
|
||||||
|
"""Returns a WebSocket client connection.
|
||||||
|
|
||||||
|
:param url: The connection URL. Both ``ws://`` and ``wss://`` URLs are
|
||||||
|
accepted.
|
||||||
|
:param subprotocols: The name of the subprotocol to use, or a list of
|
||||||
|
subprotocol names in order of preference. Set to
|
||||||
|
``None`` (the default) to not use a subprotocol.
|
||||||
|
:param headers: A dictionary or list of tuples with additional HTTP
|
||||||
|
headers to send with the connection request. Note that
|
||||||
|
custom headers are not supported by the WebSocket
|
||||||
|
protocol, so the use of this parameter is not
|
||||||
|
recommended.
|
||||||
|
:param receive_bytes: The size of the receive buffer, in bytes. The
|
||||||
|
default is 4096.
|
||||||
|
:param ping_interval: Send ping packets to the server at the requested
|
||||||
|
interval in seconds. Set to ``None`` (the
|
||||||
|
default) to disable ping/pong logic. Enable to
|
||||||
|
prevent disconnections when the line is idle for
|
||||||
|
a certain amount of time, or to detect an
|
||||||
|
unresponsive server and disconnect. A recommended
|
||||||
|
interval is 25 seconds. In general it is
|
||||||
|
preferred to enable ping/pong on the server, and
|
||||||
|
let the client respond with pong (which it does
|
||||||
|
regardless of this setting).
|
||||||
|
:param max_message_size: The maximum size allowed for a message, in
|
||||||
|
bytes, or ``None`` for no limit. The default
|
||||||
|
is ``None``.
|
||||||
|
:param ssl_context: An ``SSLContext`` instance, if a default SSL
|
||||||
|
context isn't sufficient.
|
||||||
|
:param thread_class: The ``Thread`` class to use when creating
|
||||||
|
background threads. The default is the
|
||||||
|
``threading.Thread`` class from the Python
|
||||||
|
standard library.
|
||||||
|
:param event_class: The ``Event`` class to use when creating event
|
||||||
|
objects. The default is the `threading.Event``
|
||||||
|
class from the Python standard library.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def handshake(self): # -> None:
|
||||||
|
...
|
||||||
|
def close(self, reason=..., message=...): # -> None:
|
||||||
|
...
|
||||||
Loading…
Reference in New Issue
Block a user