From 05be6fa2d84ce7a8072527e0eca0ed055795b651 Mon Sep 17 00:00:00 2001 From: Joel Bender Date: Sat, 20 Jan 2024 01:37:48 -0500 Subject: [PATCH] retrying_create_datagram_endpoint feature --- bacpypes3/__init__.py | 2 +- bacpypes3/ipv4/__init__.py | 36 +++++++++++++++++++++++++----------- bacpypes3/ipv6/__init__.py | 26 ++++++++++++++++++++------ 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/bacpypes3/__init__.py b/bacpypes3/__init__.py index 600bb53..7fed289 100644 --- a/bacpypes3/__init__.py +++ b/bacpypes3/__init__.py @@ -18,7 +18,7 @@ # Project Metadata # -__version__ = "0.0.87" +__version__ = "0.0.88" __author__ = "Joel Bender" __email__ = "joel@carrickbender.com" diff --git a/bacpypes3/ipv4/__init__.py b/bacpypes3/ipv4/__init__.py index 5e1ddac..29f5a4f 100644 --- a/bacpypes3/ipv4/__init__.py +++ b/bacpypes3/ipv4/__init__.py @@ -18,10 +18,12 @@ _debug = 0 _log = ModuleLogger(globals()) +# move this to settings sometime +BACPYPES_ENDPOINT_RETRY_INTERVAL = 1.0 + @bacpypes_debugging class IPv4DatagramProtocol(asyncio.DatagramProtocol): - _debug: Callable[..., None] server: "IPv4DatagramServer" @@ -62,7 +64,6 @@ def connection_lost(self, exc: Optional[Exception]) -> None: @bacpypes_debugging class IPv4DatagramServer(Server[PDU]): - _debug: Callable[..., None] _exception: Callable[..., None] _transport_tasks: List[Any] @@ -98,11 +99,7 @@ def __init__( # easy call to create a local endpoint local_endpoint_task = loop.create_task( - loop.create_datagram_endpoint( - IPv4DatagramProtocol, - local_addr=address.addrTuple, - allow_broadcast=True, - ) + self.retrying_create_datagram_endpoint(loop, address.addrTuple) ) if _debug: IPv4DatagramServer._debug( @@ -130,10 +127,8 @@ def __init__( # Windows takes care of the broadcast, but Linux needs a broadcast endpoint if "nt" not in os.name: broadcast_endpoint_task = loop.create_task( - loop.create_datagram_endpoint( - IPv4DatagramProtocol, - local_addr=address.addrBroadcastTuple, - allow_broadcast=True, + self.retrying_create_datagram_endpoint( + loop, address.addrBroadcastTuple ) ) if _debug: @@ -145,6 +140,25 @@ def __init__( ) self._transport_tasks.append(broadcast_endpoint_task) + async def retrying_create_datagram_endpoint( + self, loop: asyncio.events.AbstractEventLoop, addrTuple: Tuple[str, int] + ): + """ + Repeat attempts to create datagram endpoint, sometimes during boot + the interface isn't ready. Contributed by PretentiousPotatoPeeler. + """ + while True: + try: + return await loop.create_datagram_endpoint( + IPv4DatagramProtocol, local_addr=addrTuple, allow_broadcast=True + ) + except OSError: + if _debug: + IPv4DatagramServer._debug( + " - Could not create datagram endpoint, retrying..." + ) + await asyncio.sleep(BACPYPES_ENDPOINT_RETRY_INTERVAL) + def set_local_transport_protocol(self, address, task): if _debug: IPv4DatagramServer._debug( diff --git a/bacpypes3/ipv6/__init__.py b/bacpypes3/ipv6/__init__.py index 232bfc8..01d7f5e 100644 --- a/bacpypes3/ipv6/__init__.py +++ b/bacpypes3/ipv6/__init__.py @@ -18,10 +18,12 @@ _debug = 0 _log = ModuleLogger(globals()) +# move this to settings sometime +BACPYPES_ENDPOINT_RETRY_INTERVAL = 1.0 + @bacpypes_debugging class IPv6DatagramProtocol(asyncio.DatagramProtocol): - _debug: Callable[..., None] server: "IPv6DatagramServer" @@ -48,7 +50,6 @@ def connection_lost(self, exc: Optional[Exception]) -> None: @bacpypes_debugging class IPv6DatagramServer(Server[PDU]): - _debug: Callable[..., None] _exception: Callable[..., None] _transport_tasks: List[Any] @@ -101,10 +102,7 @@ def __init__( # easy call to create a local endpoint local_endpoint_task = loop.create_task( - loop.create_datagram_endpoint( - IPv6DatagramProtocol, - sock=local_socket, - ) + self.retrying_create_datagram_endpoint(loop, local_socket) ) if _debug: IPv6DatagramServer._debug( @@ -117,6 +115,22 @@ def __init__( # keep a list of things that need to complete before sending stuff self._transport_tasks = [local_endpoint_task] + async def retrying_create_datagram_endpoint( + self, loop: asyncio.events.AbstractEventLoop, local_socket: socket.socket + ): + while True: + try: + return await loop.create_datagram_endpoint( + IPv6DatagramProtocol, + sock=local_socket, + ) + except OSError: + if _debug: + IPv6DatagramServer._debug( + " - Could not create datagram endpoint, retrying..." + ) + await asyncio.sleep(BACPYPES_ENDPOINT_RETRY_INTERVAL) + def set_local_transport_protocol(self, address, task): if _debug: IPv6DatagramServer._debug(