Skip to content

Commit e3860c9

Browse files
Merge pull request #1 from tutorintelligence/adam_connection_locks
feat: send_and_receive is atomic
2 parents 2e49daa + 39e2fa3 commit e3860c9

File tree

1 file changed

+23
-16
lines changed

1 file changed

+23
-16
lines changed

adam_modbus/interface.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import asyncio
22
import socket
3+
from collections import defaultdict
34
from collections.abc import AsyncIterator
45
from contextlib import asynccontextmanager
56
from dataclasses import dataclass
6-
from typing import Literal
7+
from typing import ClassVar, Literal
78

89

910
class 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

Comments
 (0)