jmri_dccex.transport

Byte-stream transport for DCC-EX — same shape as the other drivers.

1"""Byte-stream transport for DCC-EX — same shape as the other drivers."""
2from jmri_dccex.transport.loopback import LoopbackPort, loopback_pair
3from jmri_dccex.transport.port import BytePort
4from jmri_dccex.transport.serial_port import SerialPort
5from jmri_dccex.transport.tcp_port import TcpPort
6
7__all__ = ["BytePort", "LoopbackPort", "SerialPort", "TcpPort", "loopback_pair"]
class BytePort(abc.ABC):
 8class BytePort(ABC):
 9    @abstractmethod
10    async def write(self, data: bytes) -> None: ...
11    @abstractmethod
12    async def read_byte(self) -> int: ...
13    @abstractmethod
14    async def aclose(self) -> None: ...
15    @property
16    @abstractmethod
17    def is_open(self) -> bool: ...

Helper class that provides a standard way to create an ABC using inheritance.

@abstractmethod
async def write(self, data: bytes) -> None:
 9    @abstractmethod
10    async def write(self, data: bytes) -> None: ...
@abstractmethod
async def read_byte(self) -> int:
11    @abstractmethod
12    async def read_byte(self) -> int: ...
@abstractmethod
async def aclose(self) -> None:
13    @abstractmethod
14    async def aclose(self) -> None: ...
is_open: bool
15    @property
16    @abstractmethod
17    def is_open(self) -> bool: ...
class LoopbackPort(jmri_dccex.transport.BytePort):
 9class LoopbackPort(BytePort):
10    def __init__(self, read_q: asyncio.Queue[int], write_q: asyncio.Queue[int]) -> None:
11        self._r = read_q
12        self._w = write_q
13        self._open = True
14
15    async def write(self, data: bytes) -> None:
16        if not self._open:
17            raise RuntimeError("port is closed")
18        for b in data:
19            await self._w.put(b)
20
21    async def read_byte(self) -> int:
22        return await self._r.get()
23
24    async def aclose(self) -> None:
25        self._open = False
26
27    @property
28    def is_open(self) -> bool:
29        return self._open

Helper class that provides a standard way to create an ABC using inheritance.

LoopbackPort( read_q: asyncio.queues.Queue[int], write_q: asyncio.queues.Queue[int])
10    def __init__(self, read_q: asyncio.Queue[int], write_q: asyncio.Queue[int]) -> None:
11        self._r = read_q
12        self._w = write_q
13        self._open = True
async def write(self, data: bytes) -> None:
15    async def write(self, data: bytes) -> None:
16        if not self._open:
17            raise RuntimeError("port is closed")
18        for b in data:
19            await self._w.put(b)
async def read_byte(self) -> int:
21    async def read_byte(self) -> int:
22        return await self._r.get()
async def aclose(self) -> None:
24    async def aclose(self) -> None:
25        self._open = False
is_open: bool
27    @property
28    def is_open(self) -> bool:
29        return self._open
class SerialPort(jmri_dccex.transport.BytePort):
17class SerialPort(BytePort):
18    def __init__(self, reader: StreamReader, writer: StreamWriter) -> None:
19        self._reader = reader
20        self._writer = writer
21        self._open = True
22
23    @classmethod
24    async def open(cls, port: str, baud: int = DEFAULT_BAUD) -> SerialPort:
25        import serial_asyncio  # noqa: PLC0415
26        reader, writer = await serial_asyncio.open_serial_connection(url=port, baudrate=baud)
27        return cls(reader=reader, writer=writer)
28
29    async def write(self, data: bytes) -> None:
30        if not self._open:
31            raise RuntimeError("port is closed")
32        self._writer.write(data)
33        await self._writer.drain()
34
35    async def read_byte(self) -> int:
36        data = await self._reader.readexactly(1)
37        return data[0]
38
39    async def aclose(self) -> None:
40        if not self._open:
41            return
42        self._open = False
43        self._writer.close()
44        with contextlib.suppress(Exception):
45            await self._writer.wait_closed()
46
47    @property
48    def is_open(self) -> bool:
49        return self._open

Helper class that provides a standard way to create an ABC using inheritance.

SerialPort( reader: asyncio.streams.StreamReader, writer: asyncio.streams.StreamWriter)
18    def __init__(self, reader: StreamReader, writer: StreamWriter) -> None:
19        self._reader = reader
20        self._writer = writer
21        self._open = True
@classmethod
async def open( cls, port: str, baud: int = 115200) -> SerialPort:
23    @classmethod
24    async def open(cls, port: str, baud: int = DEFAULT_BAUD) -> SerialPort:
25        import serial_asyncio  # noqa: PLC0415
26        reader, writer = await serial_asyncio.open_serial_connection(url=port, baudrate=baud)
27        return cls(reader=reader, writer=writer)
async def write(self, data: bytes) -> None:
29    async def write(self, data: bytes) -> None:
30        if not self._open:
31            raise RuntimeError("port is closed")
32        self._writer.write(data)
33        await self._writer.drain()
async def read_byte(self) -> int:
35    async def read_byte(self) -> int:
36        data = await self._reader.readexactly(1)
37        return data[0]
async def aclose(self) -> None:
39    async def aclose(self) -> None:
40        if not self._open:
41            return
42        self._open = False
43        self._writer.close()
44        with contextlib.suppress(Exception):
45            await self._writer.wait_closed()
is_open: bool
47    @property
48    def is_open(self) -> bool:
49        return self._open
class TcpPort(jmri_dccex.transport.BytePort):
15class TcpPort(BytePort):
16    def __init__(self, reader: StreamReader, writer: StreamWriter) -> None:
17        self._reader = reader
18        self._writer = writer
19        self._open = True
20
21    @classmethod
22    async def open(cls, host: str, port: int) -> TcpPort:
23        reader, writer = await asyncio.open_connection(host, port)
24        return cls(reader=reader, writer=writer)
25
26    async def write(self, data: bytes) -> None:
27        if not self._open:
28            raise RuntimeError("port is closed")
29        self._writer.write(data)
30        await self._writer.drain()
31
32    async def read_byte(self) -> int:
33        data = await self._reader.readexactly(1)
34        return data[0]
35
36    async def aclose(self) -> None:
37        if not self._open:
38            return
39        self._open = False
40        self._writer.close()
41        with contextlib.suppress(Exception):
42            await self._writer.wait_closed()
43
44    @property
45    def is_open(self) -> bool:
46        return self._open

Helper class that provides a standard way to create an ABC using inheritance.

TcpPort( reader: asyncio.streams.StreamReader, writer: asyncio.streams.StreamWriter)
16    def __init__(self, reader: StreamReader, writer: StreamWriter) -> None:
17        self._reader = reader
18        self._writer = writer
19        self._open = True
@classmethod
async def open(cls, host: str, port: int) -> TcpPort:
21    @classmethod
22    async def open(cls, host: str, port: int) -> TcpPort:
23        reader, writer = await asyncio.open_connection(host, port)
24        return cls(reader=reader, writer=writer)
async def write(self, data: bytes) -> None:
26    async def write(self, data: bytes) -> None:
27        if not self._open:
28            raise RuntimeError("port is closed")
29        self._writer.write(data)
30        await self._writer.drain()
async def read_byte(self) -> int:
32    async def read_byte(self) -> int:
33        data = await self._reader.readexactly(1)
34        return data[0]
async def aclose(self) -> None:
36    async def aclose(self) -> None:
37        if not self._open:
38            return
39        self._open = False
40        self._writer.close()
41        with contextlib.suppress(Exception):
42            await self._writer.wait_closed()
is_open: bool
44    @property
45    def is_open(self) -> bool:
46        return self._open
def loopback_pair() -> tuple[LoopbackPort, LoopbackPort]:
32def loopback_pair() -> tuple[LoopbackPort, LoopbackPort]:
33    a_to_b: asyncio.Queue[int] = asyncio.Queue()
34    b_to_a: asyncio.Queue[int] = asyncio.Queue()
35    return LoopbackPort(b_to_a, a_to_b), LoopbackPort(a_to_b, b_to_a)