moat-lib-stream 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- moat_lib_stream-0.1.2/LICENSE.txt +14 -0
- moat_lib_stream-0.1.2/PKG-INFO +68 -0
- moat_lib_stream-0.1.2/README.md +47 -0
- moat_lib_stream-0.1.2/debian/control +21 -0
- moat_lib_stream-0.1.2/debian/rules +5 -0
- moat_lib_stream-0.1.2/pyproject.toml +35 -0
- moat_lib_stream-0.1.2/setup.cfg +4 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/__init__.py +95 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/_cbor.py +148 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/_console.py +62 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/_serial.py +72 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/anyio.py +285 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/base.py +323 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/cbor.py +35 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/log.py +160 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/reliable.py +624 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/tcp.py +60 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/terminal.py +161 -0
- moat_lib_stream-0.1.2/src/moat/lib/stream/unix.py +33 -0
- moat_lib_stream-0.1.2/src/moat_lib_stream.egg-info/PKG-INFO +68 -0
- moat_lib_stream-0.1.2/src/moat_lib_stream.egg-info/SOURCES.txt +22 -0
- moat_lib_stream-0.1.2/src/moat_lib_stream.egg-info/dependency_links.txt +1 -0
- moat_lib_stream-0.1.2/src/moat_lib_stream.egg-info/requires.txt +2 -0
- moat_lib_stream-0.1.2/src/moat_lib_stream.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
The code in this repository, and all MoaT submodules it refers to,
|
|
2
|
+
is part of the MoaT project.
|
|
3
|
+
|
|
4
|
+
Unless a submodule's LICENSE.txt states otherwise, all included files are
|
|
5
|
+
licensed under the LGPL V3, as published by the FSF at
|
|
6
|
+
https://www.gnu.org/licenses/lgpl-3.0.html .
|
|
7
|
+
|
|
8
|
+
In addition to the LGPL's terms, the author(s) respectfully ask all users of
|
|
9
|
+
this code to contribute any bug fixes or enhancements. Also, please link back to
|
|
10
|
+
https://M-o-a-T.org.
|
|
11
|
+
|
|
12
|
+
Thank you.
|
|
13
|
+
|
|
14
|
+
Copyright © 2021 ff.: the MoaT contributor(s), as per the git changelog(s).
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: moat-lib-stream
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Stream infrastructure for MoaT applications
|
|
5
|
+
Maintainer-email: Matthias Urlichs <matthias@urlichs.de>
|
|
6
|
+
Project-URL: homepage, https://m-o-a-t.org
|
|
7
|
+
Project-URL: repository, https://github.com/M-o-a-T/moat
|
|
8
|
+
Keywords: MoaT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Framework :: AnyIO
|
|
11
|
+
Classifier: Framework :: Trio
|
|
12
|
+
Classifier: Framework :: AsyncIO
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE.txt
|
|
18
|
+
Requires-Dist: moat-lib-micro~=0.1.0
|
|
19
|
+
Requires-Dist: moat-lib-rpc~=0.1.0
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# Stream Infrastructure
|
|
23
|
+
|
|
24
|
+
% start synopsis
|
|
25
|
+
% start main
|
|
26
|
+
|
|
27
|
+
This module provides base classes for handling data streams in a structured manner.
|
|
28
|
+
|
|
29
|
+
% end synopsis
|
|
30
|
+
|
|
31
|
+
## Overview
|
|
32
|
+
|
|
33
|
+
This package implements infrastructure for building layered communication stacks:
|
|
34
|
+
|
|
35
|
+
- **BaseMsg** - Message-based communication (Python objects)
|
|
36
|
+
- **BaseBlk** - Block-based communication (delimited bytestrings)
|
|
37
|
+
- **BaseBuf** - Buffer-based communication (undelimited bytestreams)
|
|
38
|
+
|
|
39
|
+
## Layered Communication Stacks
|
|
40
|
+
|
|
41
|
+
Build communication stacks by layering protocols:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from moat.lib.stream import StackedBlk
|
|
45
|
+
|
|
46
|
+
class Compression(StackedBlk):
|
|
47
|
+
async def snd(self, blk):
|
|
48
|
+
compressed = compress(blk)
|
|
49
|
+
await self.s.send(compressed)
|
|
50
|
+
|
|
51
|
+
async def rcv(self):
|
|
52
|
+
blk = await self.s.recv()
|
|
53
|
+
return decompress(blk)
|
|
54
|
+
|
|
55
|
+
# Stack layers
|
|
56
|
+
link = SerialLink(cfg) # BaseBuf
|
|
57
|
+
link = Packetizer(link, cfg) # StackedBlk
|
|
58
|
+
link = Compressor(link, cfg) # StackedBlk
|
|
59
|
+
link = Codec(link, cfg) # StackedMsg
|
|
60
|
+
async with link:
|
|
61
|
+
await link.send({"data": "hello"})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
% end main
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
This project is part of the MoaT ecosystem and is licensed under the same terms.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Stream Infrastructure
|
|
2
|
+
|
|
3
|
+
% start synopsis
|
|
4
|
+
% start main
|
|
5
|
+
|
|
6
|
+
This module provides base classes for handling data streams in a structured manner.
|
|
7
|
+
|
|
8
|
+
% end synopsis
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
This package implements infrastructure for building layered communication stacks:
|
|
13
|
+
|
|
14
|
+
- **BaseMsg** - Message-based communication (Python objects)
|
|
15
|
+
- **BaseBlk** - Block-based communication (delimited bytestrings)
|
|
16
|
+
- **BaseBuf** - Buffer-based communication (undelimited bytestreams)
|
|
17
|
+
|
|
18
|
+
## Layered Communication Stacks
|
|
19
|
+
|
|
20
|
+
Build communication stacks by layering protocols:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from moat.lib.stream import StackedBlk
|
|
24
|
+
|
|
25
|
+
class Compression(StackedBlk):
|
|
26
|
+
async def snd(self, blk):
|
|
27
|
+
compressed = compress(blk)
|
|
28
|
+
await self.s.send(compressed)
|
|
29
|
+
|
|
30
|
+
async def rcv(self):
|
|
31
|
+
blk = await self.s.recv()
|
|
32
|
+
return decompress(blk)
|
|
33
|
+
|
|
34
|
+
# Stack layers
|
|
35
|
+
link = SerialLink(cfg) # BaseBuf
|
|
36
|
+
link = Packetizer(link, cfg) # StackedBlk
|
|
37
|
+
link = Compressor(link, cfg) # StackedBlk
|
|
38
|
+
link = Codec(link, cfg) # StackedMsg
|
|
39
|
+
async with link:
|
|
40
|
+
await link.send({"data": "hello"})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
% end main
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
This project is part of the MoaT ecosystem and is licensed under the same terms.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Source: moat-lib-stream
|
|
2
|
+
Maintainer: "Matthias Urlichs" <matthias@urlichs.de>
|
|
3
|
+
Section: python
|
|
4
|
+
Priority: optional
|
|
5
|
+
Build-Depends: dh-python, python3-all, debhelper (>= 13),
|
|
6
|
+
python3-setuptools,
|
|
7
|
+
python3-wheel,
|
|
8
|
+
Standards-Version: 3.9.6
|
|
9
|
+
Homepage: https://m-o-a-t.org
|
|
10
|
+
X-DH-Compat: 13
|
|
11
|
+
|
|
12
|
+
Package: python3-moat-lib-stream
|
|
13
|
+
Architecture: all
|
|
14
|
+
Depends: ${misc:Depends}, ${python3:Depends},
|
|
15
|
+
python3-moat-lib-micro (>= 0.1),
|
|
16
|
+
python3-moat-lib-rpc (>= 0.1),
|
|
17
|
+
Description: Stream infrastructure for MoaT applications
|
|
18
|
+
This module provides base classes for handling data streams in a
|
|
19
|
+
structured manner, including message-based (BaseMsg), block-based
|
|
20
|
+
(BaseBlk), and byte stream-based (BaseBuf) communication with
|
|
21
|
+
support for stacking and logging.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "setuptools.build_meta"
|
|
3
|
+
requires = ["wheel","setuptools"]
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
classifiers = [
|
|
7
|
+
"Development Status :: 4 - Beta",
|
|
8
|
+
"Framework :: AnyIO",
|
|
9
|
+
"Framework :: Trio",
|
|
10
|
+
"Framework :: AsyncIO",
|
|
11
|
+
"Programming Language :: Python :: 3",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
]
|
|
14
|
+
dependencies = [
|
|
15
|
+
"moat-lib-micro ~= 0.1.0",
|
|
16
|
+
"moat-lib-rpc ~= 0.1.0",
|
|
17
|
+
]
|
|
18
|
+
keywords = ["MoaT"]
|
|
19
|
+
requires-python = ">=3.8"
|
|
20
|
+
name = "moat-lib-stream"
|
|
21
|
+
maintainers = [{email = "matthias@urlichs.de",name = "Matthias Urlichs"}]
|
|
22
|
+
description='Stream infrastructure for MoaT applications'
|
|
23
|
+
readme = "README.md"
|
|
24
|
+
version = "0.1.2"
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
homepage = "https://m-o-a-t.org"
|
|
28
|
+
repository = "https://github.com/M-o-a-T/moat"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools]
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.package-data]
|
|
35
|
+
"*" = ["*.yaml"]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Stream infrastructure for handling data streams in a structured manner.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
# Import base classes directly (not lazy)
|
|
8
|
+
from .base import Base as Base
|
|
9
|
+
from .base import BaseBlk as BaseBlk
|
|
10
|
+
from .base import BaseBuf as BaseBuf
|
|
11
|
+
from .base import BaseConn as BaseConn
|
|
12
|
+
from .base import BaseMsg as BaseMsg
|
|
13
|
+
from .base import StackedBlk as StackedBlk
|
|
14
|
+
from .base import StackedBuf as StackedBuf
|
|
15
|
+
from .base import StackedConn as StackedConn
|
|
16
|
+
from .base import StackedMsg as StackedMsg
|
|
17
|
+
|
|
18
|
+
from typing import TYPE_CHECKING as _TC
|
|
19
|
+
|
|
20
|
+
if _TC:
|
|
21
|
+
from .anyio import AnyioBuf as AnyioBuf
|
|
22
|
+
from .anyio import BufAnyio as BufAnyio
|
|
23
|
+
from .anyio import FilenoBuf as FilenoBuf
|
|
24
|
+
from .anyio import ProcessBuf as ProcessBuf
|
|
25
|
+
from .anyio import ProcessDeadError as ProcessDeadError
|
|
26
|
+
from .anyio import RemoteBufAnyio as RemoteBufAnyio
|
|
27
|
+
from .anyio import SingleAnyioBuf as SingleAnyioBuf
|
|
28
|
+
from .cbor import CBORMsgBlk as CBORMsgBlk
|
|
29
|
+
from .cbor import CBORMsgBuf as CBORMsgBuf
|
|
30
|
+
from .log import LogBlk as LogBlk
|
|
31
|
+
from .log import LogBuf as LogBuf
|
|
32
|
+
from .log import LogMsg as LogMsg
|
|
33
|
+
from .reliable import EphemeralMsg as EphemeralMsg
|
|
34
|
+
from .reliable import ReliableMsg as ReliableMsg
|
|
35
|
+
from .tcp import TcpLink as TcpLink
|
|
36
|
+
from .terminal import FilenoTerm as FilenoTerm
|
|
37
|
+
from .terminal import TermBuf as TermBuf
|
|
38
|
+
from .unix import UnixLink as UnixLink
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Lazy loading
|
|
42
|
+
_imports = {
|
|
43
|
+
# Logging
|
|
44
|
+
"LogMsg": "log",
|
|
45
|
+
"LogBlk": "log",
|
|
46
|
+
"LogBuf": "log",
|
|
47
|
+
# CBOR
|
|
48
|
+
"CBORMsgBuf": "cbor",
|
|
49
|
+
"CBORMsgBlk": "cbor",
|
|
50
|
+
# AnyIO
|
|
51
|
+
"ProcessDeadError": "anyio",
|
|
52
|
+
"AnyioBuf": "anyio",
|
|
53
|
+
"FilenoBuf": "anyio",
|
|
54
|
+
"RemoteBufAnyio": "anyio",
|
|
55
|
+
"BufAnyio": "anyio",
|
|
56
|
+
"SingleAnyioBuf": "anyio",
|
|
57
|
+
"ProcessBuf": "anyio",
|
|
58
|
+
# Reliable messaging
|
|
59
|
+
"ReliableMsg": "reliable",
|
|
60
|
+
"EphemeralMsg": "reliable",
|
|
61
|
+
# Network connections
|
|
62
|
+
"TcpLink": "tcp",
|
|
63
|
+
"UnixLink": "unix",
|
|
64
|
+
# Terminal
|
|
65
|
+
"FilenoTerm": "terminal",
|
|
66
|
+
"TermBuf": "terminal",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def __getattr__(attr: str):
|
|
71
|
+
try:
|
|
72
|
+
mod = _imports[attr]
|
|
73
|
+
except KeyError:
|
|
74
|
+
raise AttributeError(attr) from None
|
|
75
|
+
value = getattr(__import__(mod, globals(), None, True, 1), attr)
|
|
76
|
+
globals()[attr] = value
|
|
77
|
+
return value
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
__all__ = [
|
|
81
|
+
# Base classes (not lazy)
|
|
82
|
+
"Base",
|
|
83
|
+
"BaseBlk",
|
|
84
|
+
"BaseBuf",
|
|
85
|
+
"BaseConn",
|
|
86
|
+
"BaseMsg",
|
|
87
|
+
"StackedBlk",
|
|
88
|
+
"StackedBuf",
|
|
89
|
+
"StackedConn",
|
|
90
|
+
"StackedMsg",
|
|
91
|
+
] + list(_imports.keys())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def __dir__():
|
|
95
|
+
return __all__
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CBOR message encoding/decoding for stream layers.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from moat.lib.micro import AC_use, Lock, log
|
|
8
|
+
from moat.lib.stream import BaseBuf, StackedMsg
|
|
9
|
+
|
|
10
|
+
from ._console import _CReader
|
|
11
|
+
|
|
12
|
+
from typing import TYPE_CHECKING # isort:skip
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from moat.lib.codec import Codec
|
|
16
|
+
from moat.util.liner import Liner
|
|
17
|
+
|
|
18
|
+
from collections.abc import Awaitable
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _CBORMsgBuf(StackedMsg):
|
|
23
|
+
"""
|
|
24
|
+
structured messages > CBOR bytestream
|
|
25
|
+
|
|
26
|
+
Use this if your stream is reliable (TCP, USB, …) but doesn't support
|
|
27
|
+
message boundaries.
|
|
28
|
+
|
|
29
|
+
If @console is set and a prefix is used, sends data atomically.
|
|
30
|
+
Otherwise two separate write calls are used to save on message copying.
|
|
31
|
+
|
|
32
|
+
Config:
|
|
33
|
+
console (bool):
|
|
34
|
+
Flag how to handle non-framed data.
|
|
35
|
+
True: collect for crd/cwr, False: print incoming, None: ignore.
|
|
36
|
+
msg_prefix(int):
|
|
37
|
+
bytecode of prefix for messages (as opposed to console data)
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
cons = False
|
|
41
|
+
codec: Codec = None
|
|
42
|
+
liner: Liner = None
|
|
43
|
+
|
|
44
|
+
def __init__(self, stream: BaseBuf, cfg: dict):
|
|
45
|
+
#
|
|
46
|
+
# console: size of console buffer, 128 if True
|
|
47
|
+
# msg_prefix: int: code for start-of-packet
|
|
48
|
+
#
|
|
49
|
+
super().__init__(stream, cfg)
|
|
50
|
+
self.w_lock = Lock()
|
|
51
|
+
|
|
52
|
+
pref = cfg.get("msg_prefix")
|
|
53
|
+
if pref is not None:
|
|
54
|
+
pref = bytes((pref,))
|
|
55
|
+
self.pref = pref
|
|
56
|
+
|
|
57
|
+
cons = cfg.get("console", False)
|
|
58
|
+
if cons:
|
|
59
|
+
_CReader.__init__(self, cons)
|
|
60
|
+
|
|
61
|
+
async def setup(self):
|
|
62
|
+
await super().setup()
|
|
63
|
+
if self.cons is False:
|
|
64
|
+
from moat.util.liner import Liner # noqa:PLC0415
|
|
65
|
+
|
|
66
|
+
self.liner = await AC_use(self, Liner())
|
|
67
|
+
|
|
68
|
+
async def cwr(self, buf):
|
|
69
|
+
if not self.cons:
|
|
70
|
+
return
|
|
71
|
+
return await self.s.wr(buf)
|
|
72
|
+
|
|
73
|
+
def crd(self, buf) -> Awaitable:
|
|
74
|
+
return _CReader.crd(self, buf)
|
|
75
|
+
|
|
76
|
+
async def send(self, msg: Any) -> None:
|
|
77
|
+
try:
|
|
78
|
+
msg = self.codec.encode(msg)
|
|
79
|
+
except Exception:
|
|
80
|
+
log("MSG:\n%r", msg)
|
|
81
|
+
raise
|
|
82
|
+
async with self.w_lock:
|
|
83
|
+
if self.pref is not None:
|
|
84
|
+
if True: # self.cons:
|
|
85
|
+
msg = self.pref + msg # must be atomic
|
|
86
|
+
else:
|
|
87
|
+
await self.s.wr(self.pref)
|
|
88
|
+
await self.s.wr(msg)
|
|
89
|
+
|
|
90
|
+
async def recv(self) -> Any:
|
|
91
|
+
"""
|
|
92
|
+
Receive the next object.
|
|
93
|
+
"""
|
|
94
|
+
# Pre+postcondition: the codec does not have an object in progress.
|
|
95
|
+
|
|
96
|
+
buf = bytearray(64)
|
|
97
|
+
if self.pref is None:
|
|
98
|
+
# easy case
|
|
99
|
+
while True:
|
|
100
|
+
try:
|
|
101
|
+
r = next(self.codec)
|
|
102
|
+
except StopIteration:
|
|
103
|
+
n = await self.s.rd(buf)
|
|
104
|
+
self.codec.feed(memoryview(buf)[:n])
|
|
105
|
+
else:
|
|
106
|
+
if self.cons and isinstance(r, int) and r >= 0:
|
|
107
|
+
_CReader.cput(self, r)
|
|
108
|
+
else:
|
|
109
|
+
return r
|
|
110
|
+
|
|
111
|
+
while True:
|
|
112
|
+
b = bytearray(1)
|
|
113
|
+
# read until we get a prefix byte
|
|
114
|
+
if self.codec.unfeed(b) == 0:
|
|
115
|
+
n = await self.s.rd(buf)
|
|
116
|
+
self.codec.feed(memoryview(buf)[:n])
|
|
117
|
+
elif b == self.pref:
|
|
118
|
+
break
|
|
119
|
+
elif self.cons:
|
|
120
|
+
_CReader.cput(self, b[0])
|
|
121
|
+
elif self.liner is not None:
|
|
122
|
+
self.liner(b)
|
|
123
|
+
|
|
124
|
+
while True:
|
|
125
|
+
# read until we get an object
|
|
126
|
+
try:
|
|
127
|
+
return next(self.codec)
|
|
128
|
+
except StopIteration:
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
n = await self.s.rd(buf)
|
|
132
|
+
self.codec.feed(memoryview(buf)[:n])
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class _CBORMsgBlk(StackedMsg):
|
|
136
|
+
"""
|
|
137
|
+
structured messages > chunked bytestrings
|
|
138
|
+
|
|
139
|
+
Use this if the layer below supports byte boundaries
|
|
140
|
+
(one bytestring-ized message per call).
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
async def send(self, msg):
|
|
144
|
+
await self.s.snd(self.codec.encode(msg))
|
|
145
|
+
|
|
146
|
+
async def recv(self):
|
|
147
|
+
m = await self.s.rcv()
|
|
148
|
+
return self.codec.decode(m)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Console data handling for stream layers.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from moat.lib.micro import Event
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _CReader:
|
|
11
|
+
"""
|
|
12
|
+
A mix-in that processes incoming console data.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, cons: bool | int):
|
|
16
|
+
if cons is True:
|
|
17
|
+
try:
|
|
18
|
+
import machine # noqa: PLC0415,F401
|
|
19
|
+
except ImportError:
|
|
20
|
+
cons = 32768
|
|
21
|
+
else:
|
|
22
|
+
cons = 240
|
|
23
|
+
self.cevt = Event()
|
|
24
|
+
self.cpos = 0
|
|
25
|
+
self.cbuf = bytearray(cons)
|
|
26
|
+
self.cons = cons
|
|
27
|
+
self.intr = 0
|
|
28
|
+
|
|
29
|
+
async def crd(self, buf: bytearray):
|
|
30
|
+
"""read waiting console data"""
|
|
31
|
+
if not self.cons:
|
|
32
|
+
raise EOFError
|
|
33
|
+
if not self.cpos:
|
|
34
|
+
await self.cevt.wait()
|
|
35
|
+
self.cevt = Event()
|
|
36
|
+
n = min(len(buf), self.cpos)
|
|
37
|
+
buf[:n] = self.cbuf[:n]
|
|
38
|
+
if n < self.cpos:
|
|
39
|
+
self.cbuf[: self.cpos - n] = self.cbuf[n : self.cpos]
|
|
40
|
+
self.cpos -= n
|
|
41
|
+
else:
|
|
42
|
+
self.cpos = 0
|
|
43
|
+
return n
|
|
44
|
+
|
|
45
|
+
def cput(self, b: int):
|
|
46
|
+
"""store a byte in the console buffer"""
|
|
47
|
+
if self.cpos == len(self.cbuf):
|
|
48
|
+
if len(self.cbuf) > 100:
|
|
49
|
+
bfull = b"\n?BUF\n"
|
|
50
|
+
self.cbuf[0 : len(bfull)] = bfull
|
|
51
|
+
self.cpos = len(bfull)
|
|
52
|
+
else:
|
|
53
|
+
self.cpos = 0
|
|
54
|
+
if b != 3:
|
|
55
|
+
self.intr = 0
|
|
56
|
+
elif self.intr > 2:
|
|
57
|
+
raise KeyboardInterrupt
|
|
58
|
+
else:
|
|
59
|
+
self.intr += 1
|
|
60
|
+
self.cbuf[self.cpos] = b
|
|
61
|
+
self.cpos += 1
|
|
62
|
+
self.cevt.set()
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SerialPacker protocol support for stream layers.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from moat.lib.micro import Lock
|
|
8
|
+
from moat.lib.stream import BaseBuf, StackedBlk
|
|
9
|
+
|
|
10
|
+
from ._console import _CReader
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SerialPackerBlkBuf(StackedBlk):
|
|
14
|
+
"""
|
|
15
|
+
chunked bytestrings > SerialPacker-ized stream
|
|
16
|
+
|
|
17
|
+
Use this (and a CBORHandler and a Reliable) if your AIO stream
|
|
18
|
+
is unreliable (TTL serial).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
cons = False
|
|
22
|
+
|
|
23
|
+
def __init__(self, stream: BaseBuf, frame: dict, console: bool | int = False):
|
|
24
|
+
super().__init__(None)
|
|
25
|
+
|
|
26
|
+
from serialpacker import SerialPacker # noqa: PLC0415
|
|
27
|
+
|
|
28
|
+
self.s = stream
|
|
29
|
+
self.p = SerialPacker(**frame)
|
|
30
|
+
self.buf = bytearray(16)
|
|
31
|
+
self.i = 0
|
|
32
|
+
self.n = 0
|
|
33
|
+
self.w_lock = Lock()
|
|
34
|
+
if console:
|
|
35
|
+
_CReader.__init__(self, console)
|
|
36
|
+
|
|
37
|
+
async def crd(self, buf):
|
|
38
|
+
if not self.cons:
|
|
39
|
+
raise EOFError
|
|
40
|
+
return await _CReader.crd(self, buf)
|
|
41
|
+
|
|
42
|
+
async def cwr(self, buf):
|
|
43
|
+
if not self.cons:
|
|
44
|
+
return
|
|
45
|
+
return await super().wr(buf)
|
|
46
|
+
|
|
47
|
+
async def recv(self):
|
|
48
|
+
while True:
|
|
49
|
+
while self.i < self.n:
|
|
50
|
+
msg = self.p.feed(self.buf[self.i])
|
|
51
|
+
self.i += 1
|
|
52
|
+
if isinstance(msg, int):
|
|
53
|
+
if self.cons:
|
|
54
|
+
_CReader.cput(self, msg)
|
|
55
|
+
elif msg is not None:
|
|
56
|
+
return msg
|
|
57
|
+
|
|
58
|
+
n = await self.s.rd(self.buf)
|
|
59
|
+
if not n:
|
|
60
|
+
raise EOFError
|
|
61
|
+
self.i = 0
|
|
62
|
+
self.n = n
|
|
63
|
+
|
|
64
|
+
async def send(self, msg):
|
|
65
|
+
h, msg, t = self.p.frame(msg)
|
|
66
|
+
async with self.w_lock:
|
|
67
|
+
if not self.cons:
|
|
68
|
+
await self.s.wr(h)
|
|
69
|
+
await self.s.wr(msg)
|
|
70
|
+
await self.s.wr(t)
|
|
71
|
+
else:
|
|
72
|
+
await self.s.wr(h + msg + t)
|