11import asyncio
22import socket
3+ from collections import defaultdict
34from collections .abc import AsyncIterator
45from contextlib import asynccontextmanager
56from dataclasses import dataclass
6- from typing import Literal
7+ from typing import ClassVar , Literal
78
89
910class AdamConnectionError (RuntimeError ):
@@ -22,22 +23,28 @@ class AdamConnection:
2223 timeout : float = ADAM_CONNECTION_TIMEOUT
2324 model : str | None = None
2425
25- async def _send_and_receive (self , message : str ) -> str :
26- loop = asyncio .get_running_loop ()
26+ # ClassVar so it's shared among all AdamConnections
27+ ADAM_CONNECTION_LOCKS : ClassVar = defaultdict [tuple [str , int ], asyncio .Lock ](
28+ lambda : asyncio .Lock ()
29+ )
2730
28- try :
29- await asyncio .wait_for (
30- loop .sock_sendall (self .socket , message .encode ("ascii" )),
31- self .timeout ,
32- )
33- adam_out = await asyncio .wait_for (
34- loop .sock_recv (self .socket , 100 ), self .timeout
35- )
36- except asyncio .TimeoutError :
37- raise AdamConnectionError ("ADAM connection timed out" )
38-
39- response = adam_out .decode ().strip ()
40- return response
31+ async def _send_and_receive (self , message : str ) -> str :
32+ async with self .ADAM_CONNECTION_LOCKS [(self .ip , self .port )]:
33+ loop = asyncio .get_running_loop ()
34+
35+ try :
36+ await asyncio .wait_for (
37+ loop .sock_sendall (self .socket , message .encode ("ascii" )),
38+ self .timeout ,
39+ )
40+ adam_out = await asyncio .wait_for (
41+ loop .sock_recv (self .socket , 100 ), self .timeout
42+ )
43+ except asyncio .TimeoutError :
44+ raise AdamConnectionError ("ADAM connection timed out" )
45+
46+ response = adam_out .decode ().strip ()
47+ return response
4148
4249 async def set_digital_out (
4350 self ,
0 commit comments