ramses-rf 0.53.3__py3-none-any.whl → 0.53.4__py3-none-any.whl
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.
- ramses_cli/client.py +15 -11
- ramses_rf/gateway.py +14 -10
- ramses_rf/version.py +1 -1
- {ramses_rf-0.53.3.dist-info → ramses_rf-0.53.4.dist-info}/METADATA +1 -1
- {ramses_rf-0.53.3.dist-info → ramses_rf-0.53.4.dist-info}/RECORD +11 -11
- ramses_tx/gateway.py +20 -16
- ramses_tx/transport.py +29 -15
- ramses_tx/version.py +1 -1
- {ramses_rf-0.53.3.dist-info → ramses_rf-0.53.4.dist-info}/WHEEL +0 -0
- {ramses_rf-0.53.3.dist-info → ramses_rf-0.53.4.dist-info}/entry_points.txt +0 -0
- {ramses_rf-0.53.3.dist-info → ramses_rf-0.53.4.dist-info}/licenses/LICENSE +0 -0
ramses_cli/client.py
CHANGED
|
@@ -471,13 +471,8 @@ def print_results(gwy: Gateway, **kwargs: Any) -> None:
|
|
|
471
471
|
system_id, _ = kwargs[GET_SCHED]
|
|
472
472
|
|
|
473
473
|
|
|
474
|
-
def
|
|
475
|
-
"""
|
|
476
|
-
|
|
477
|
-
:param gwy: The gateway instance.
|
|
478
|
-
"""
|
|
479
|
-
schema, msgs = gwy.get_state()
|
|
480
|
-
|
|
474
|
+
def _write_state(schema: dict[str, Any], msgs: dict[str, str]) -> None:
|
|
475
|
+
"""Write the state to the file system (blocking)."""
|
|
481
476
|
with open("state_msgs.log", "w") as f:
|
|
482
477
|
[f.write(f"{dtm} {pkt}\r\n") for dtm, pkt in msgs.items()] # if not m._expired
|
|
483
478
|
|
|
@@ -485,13 +480,22 @@ def _save_state(gwy: Gateway) -> None:
|
|
|
485
480
|
f.write(json.dumps(schema, indent=4))
|
|
486
481
|
|
|
487
482
|
|
|
488
|
-
def
|
|
483
|
+
async def _save_state(gwy: Gateway) -> None:
|
|
484
|
+
"""Save the gateway state to files.
|
|
485
|
+
|
|
486
|
+
:param gwy: The gateway instance.
|
|
487
|
+
"""
|
|
488
|
+
schema, msgs = await gwy.get_state()
|
|
489
|
+
await asyncio.to_thread(_write_state, schema, msgs)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
async def _print_engine_state(gwy: Gateway, **kwargs: Any) -> None:
|
|
489
493
|
"""Print the current engine state (schema and packets).
|
|
490
494
|
|
|
491
495
|
:param gwy: The gateway instance.
|
|
492
496
|
:param kwargs: Command arguments to determine verbosity.
|
|
493
497
|
"""
|
|
494
|
-
(schema, packets) = gwy.get_state(include_expired=True)
|
|
498
|
+
(schema, packets) = await gwy.get_state(include_expired=True)
|
|
495
499
|
|
|
496
500
|
if kwargs["print_state"] > 0:
|
|
497
501
|
print(f"schema: {json.dumps(schema, indent=4)}\r\n")
|
|
@@ -671,10 +675,10 @@ async def async_main(command: str, lib_kwargs: dict[str, Any], **kwargs: Any) ->
|
|
|
671
675
|
print(f"\r\nclient.py: Engine stopped: {msg}")
|
|
672
676
|
|
|
673
677
|
# if kwargs["save_state"]:
|
|
674
|
-
# _save_state(gwy)
|
|
678
|
+
# await _save_state(gwy)
|
|
675
679
|
|
|
676
680
|
if kwargs["print_state"]:
|
|
677
|
-
_print_engine_state(gwy, **kwargs)
|
|
681
|
+
await _print_engine_state(gwy, **kwargs)
|
|
678
682
|
|
|
679
683
|
elif command == EXECUTE:
|
|
680
684
|
print_results(gwy, **kwargs)
|
ramses_rf/gateway.py
CHANGED
|
@@ -267,12 +267,14 @@ class Gateway(Engine):
|
|
|
267
267
|
:returns: None
|
|
268
268
|
:rtype: None
|
|
269
269
|
"""
|
|
270
|
+
# Stop the Engine first to ensure no tasks/callbacks try to write
|
|
271
|
+
# to the DB while we are closing it.
|
|
272
|
+
await super().stop()
|
|
270
273
|
|
|
271
274
|
if self.msg_db:
|
|
272
275
|
self.msg_db.stop()
|
|
273
|
-
await super().stop()
|
|
274
276
|
|
|
275
|
-
def _pause(self, *args: Any) -> None:
|
|
277
|
+
async def _pause(self, *args: Any) -> None:
|
|
276
278
|
"""Pause the (unpaused) gateway (disables sending/discovery).
|
|
277
279
|
|
|
278
280
|
There is the option to save other objects, as `args`.
|
|
@@ -288,12 +290,12 @@ class Gateway(Engine):
|
|
|
288
290
|
self.config.disable_discovery, disc_flag = True, self.config.disable_discovery
|
|
289
291
|
|
|
290
292
|
try:
|
|
291
|
-
super()._pause(disc_flag, *args)
|
|
293
|
+
await super()._pause(disc_flag, *args)
|
|
292
294
|
except RuntimeError:
|
|
293
295
|
self.config.disable_discovery = disc_flag
|
|
294
296
|
raise
|
|
295
297
|
|
|
296
|
-
def _resume(self) -> tuple[Any]:
|
|
298
|
+
async def _resume(self) -> tuple[Any]:
|
|
297
299
|
"""Resume the (paused) gateway (enables sending/discovery, if applicable).
|
|
298
300
|
|
|
299
301
|
Will restore other objects, as `args`.
|
|
@@ -305,11 +307,13 @@ class Gateway(Engine):
|
|
|
305
307
|
|
|
306
308
|
_LOGGER.debug("Gateway: Resuming engine...")
|
|
307
309
|
|
|
308
|
-
|
|
310
|
+
# args_tuple = await super()._resume()
|
|
311
|
+
# self.config.disable_discovery, *args = args_tuple # type: ignore[assignment]
|
|
312
|
+
self.config.disable_discovery, *args = await super()._resume() # type: ignore[assignment]
|
|
309
313
|
|
|
310
314
|
return args
|
|
311
315
|
|
|
312
|
-
def get_state(
|
|
316
|
+
async def get_state(
|
|
313
317
|
self, include_expired: bool = False
|
|
314
318
|
) -> tuple[dict[str, Any], dict[str, str]]:
|
|
315
319
|
"""Return the current schema & state (may include expired packets).
|
|
@@ -320,7 +324,7 @@ class Gateway(Engine):
|
|
|
320
324
|
:rtype: tuple[dict[str, Any], dict[str, str]]
|
|
321
325
|
"""
|
|
322
326
|
|
|
323
|
-
self._pause()
|
|
327
|
+
await self._pause()
|
|
324
328
|
|
|
325
329
|
def wanted_msg(msg: Message, include_expired: bool = False) -> bool:
|
|
326
330
|
if msg.code == Code._313F:
|
|
@@ -357,7 +361,7 @@ class Gateway(Engine):
|
|
|
357
361
|
}
|
|
358
362
|
# _LOGGER.warning("Missing MessageIndex")
|
|
359
363
|
|
|
360
|
-
self._resume()
|
|
364
|
+
await self._resume()
|
|
361
365
|
|
|
362
366
|
return self.schema, dict(sorted(pkts.items()))
|
|
363
367
|
|
|
@@ -392,7 +396,7 @@ class Gateway(Engine):
|
|
|
392
396
|
tmp_transport: RamsesTransportT # mypy hint
|
|
393
397
|
|
|
394
398
|
_LOGGER.debug("Gateway: Restoring a cached packet log...")
|
|
395
|
-
self._pause()
|
|
399
|
+
await self._pause()
|
|
396
400
|
|
|
397
401
|
if _clear_state: # only intended for test suite use
|
|
398
402
|
clear_state()
|
|
@@ -428,7 +432,7 @@ class Gateway(Engine):
|
|
|
428
432
|
await tmp_transport.get_extra_info(SZ_READER_TASK)
|
|
429
433
|
|
|
430
434
|
_LOGGER.debug("Gateway: Restored, resuming")
|
|
431
|
-
self._resume()
|
|
435
|
+
await self._resume()
|
|
432
436
|
|
|
433
437
|
def _add_device(self, dev: Device) -> None: # TODO: also: _add_system()
|
|
434
438
|
"""Add a device to the gateway (called by devices during instantiation).
|
ramses_rf/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ramses_rf
|
|
3
|
-
Version: 0.53.
|
|
3
|
+
Version: 0.53.4
|
|
4
4
|
Summary: A stateful RAMSES-II protocol decoder & analyser.
|
|
5
5
|
Project-URL: Homepage, https://github.com/ramses-rf/ramses_rf
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/ramses-rf/ramses_rf/issues
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
ramses_cli/__init__.py,sha256=d_3uIFkK8JnWOxknrBloKCe6-vI9Ouo_KGqR4kfBQW8,417
|
|
2
|
-
ramses_cli/client.py,sha256=
|
|
2
|
+
ramses_cli/client.py,sha256=w95Xv2_kVlYelI5XnGt6D2QVLG3guiSMqo_MO1Ni-dc,25277
|
|
3
3
|
ramses_cli/debug.py,sha256=PLcz-3PjUiMVqtD_p6VqTA92eHUM58lOBFXh_qgQ_wA,576
|
|
4
4
|
ramses_cli/discovery.py,sha256=WTcoFH5hNhQ1AeOZtpdZIVYwdUfmUKlq2iBpa-KcgoI,12512
|
|
5
5
|
ramses_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -12,12 +12,12 @@ ramses_rf/database.py,sha256=Fv3Xv6S_7qOf6-biHHKZvntkB8ps_SJvjPlKX0pzGfg,23919
|
|
|
12
12
|
ramses_rf/dispatcher.py,sha256=YjEU-QrBLo9IfoEhJo2ikg_FxOaMYoWvzelr9Vi-JZ8,11398
|
|
13
13
|
ramses_rf/entity_base.py,sha256=L47P_6CRz3tLDzOzII9AgmueKDb-Bp7Ot3vVsr8jo10,59121
|
|
14
14
|
ramses_rf/exceptions.py,sha256=mt_T7irqHSDKir6KLaf6oDglUIdrw0S40JbOrWJk5jc,3657
|
|
15
|
-
ramses_rf/gateway.py,sha256=
|
|
15
|
+
ramses_rf/gateway.py,sha256=3rnKm-OunN-O_T0eS9ZayEvg49WMVe7GF4pJhyGx3Io,30409
|
|
16
16
|
ramses_rf/helpers.py,sha256=TNk_QkpIOB3alOp1sqnA9LOzi4fuDCeapNlW3zTzNas,4250
|
|
17
17
|
ramses_rf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
18
|
ramses_rf/schemas.py,sha256=X1GAK3kttuLMiSCUDY2s-85fgBxPeU8xiDa6gJ1I5mY,13543
|
|
19
19
|
ramses_rf/storage.py,sha256=lGKUgQzXBUwksmEeHMLoVoKPCMPAWWuzwCXM8G2CKmg,6452
|
|
20
|
-
ramses_rf/version.py,sha256=
|
|
20
|
+
ramses_rf/version.py,sha256=4QvpG_-UTxyow-YNsbbOguS-J5cqoyPKHelB0WfKMkA,125
|
|
21
21
|
ramses_rf/device/__init__.py,sha256=sUbH5dhbYFXSoM_TPFRutpRutBRpup7_cQ9smPtDTy8,4858
|
|
22
22
|
ramses_rf/device/base.py,sha256=Tu5I8Lj7KplfRsIBQAYjilS6YPgTyjpU8qgKugMR2Jk,18281
|
|
23
23
|
ramses_rf/device/heat.py,sha256=CU6GlIgjuYD21braJ_RJlS56zP47TGXNxXnZeavfEMY,54654
|
|
@@ -34,7 +34,7 @@ ramses_tx/const.py,sha256=jiE2UaGBJ5agr68EMrcEHWtVz2KMidU7c7rRYCIiaoM,33010
|
|
|
34
34
|
ramses_tx/exceptions.py,sha256=FJSU9YkvpKjs3yeTqUJX1o3TPFSe_B01gRGIh9b3PNc,2632
|
|
35
35
|
ramses_tx/fingerprints.py,sha256=nfftA1E62HQnb-eLt2EqjEi_la0DAoT0wt-PtTMie0s,11974
|
|
36
36
|
ramses_tx/frame.py,sha256=GzNsXr15YLeidJYGtk_xPqsZQh4ehDDlUCtT6rTDhT8,22046
|
|
37
|
-
ramses_tx/gateway.py,sha256=
|
|
37
|
+
ramses_tx/gateway.py,sha256=kJv3jRI66Ii-kxZO6ItbPODW2oJf-e46ou2JdQt0yTY,11498
|
|
38
38
|
ramses_tx/helpers.py,sha256=96OvSOWYuMcr89_c-3dRnqHZaMOctCO94uo1hETh3bc,33613
|
|
39
39
|
ramses_tx/logger.py,sha256=1iKRHKUaqHqGd76CkE_6mCVR0sYODtxshRRwfY61fTk,10426
|
|
40
40
|
ramses_tx/message.py,sha256=zsyDQztSUYeqj3-P598LSmy9ODQY2BUCzWxSoZds6bM,13953
|
|
@@ -46,12 +46,12 @@ ramses_tx/protocol_fsm.py,sha256=o9vLvlXor3LkPgsY1zii5P1R01GzYLf_PECDdoxtC24,275
|
|
|
46
46
|
ramses_tx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
47
|
ramses_tx/ramses.py,sha256=V4LqD6IaohU7TTZp-_f1K2SOCJwzRY0v8_-INESh2cU,53986
|
|
48
48
|
ramses_tx/schemas.py,sha256=Hrmf_q9bAZtkKJzGu6GtUO0QV_-K9i4L99EzGWR13eE,13408
|
|
49
|
-
ramses_tx/transport.py,sha256=
|
|
49
|
+
ramses_tx/transport.py,sha256=RIrcNrJwiKB_xmJLgG4Z--V2d83PLsJnLXZK-WFFgsA,76568
|
|
50
50
|
ramses_tx/typed_dicts.py,sha256=w-0V5t2Q3GiNUOrRAWiW9GtSwbta_7luME6GfIb1zhI,10869
|
|
51
51
|
ramses_tx/typing.py,sha256=eF2SlPWhNhEFQj6WX2AhTXiyRQVXYnFutiepllYl2rI,5042
|
|
52
|
-
ramses_tx/version.py,sha256=
|
|
53
|
-
ramses_rf-0.53.
|
|
54
|
-
ramses_rf-0.53.
|
|
55
|
-
ramses_rf-0.53.
|
|
56
|
-
ramses_rf-0.53.
|
|
57
|
-
ramses_rf-0.53.
|
|
52
|
+
ramses_tx/version.py,sha256=hutrhdcMJOwR4LO2siGwJHfwUmCNdXAWpBV8-SaqUVo,123
|
|
53
|
+
ramses_rf-0.53.4.dist-info/METADATA,sha256=GNBabuxJgwv1H80fzFeCO31BSn0yI76V0cTeMKp2HjE,4179
|
|
54
|
+
ramses_rf-0.53.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
55
|
+
ramses_rf-0.53.4.dist-info/entry_points.txt,sha256=NnyK29baOCNg8DinPYiZ368h7MTH7bgTW26z2A1NeIE,50
|
|
56
|
+
ramses_rf-0.53.4.dist-info/licenses/LICENSE,sha256=ptVutrtSMr7X-ek6LduiD8Cce4JsNn_8sR8MYlm-fvo,1086
|
|
57
|
+
ramses_rf-0.53.4.dist-info/RECORD,,
|
ramses_tx/gateway.py
CHANGED
|
@@ -12,7 +12,6 @@ import asyncio
|
|
|
12
12
|
import logging
|
|
13
13
|
from collections.abc import Callable
|
|
14
14
|
from datetime import datetime as dt
|
|
15
|
-
from threading import Lock
|
|
16
15
|
from typing import TYPE_CHECKING, Any, Never
|
|
17
16
|
|
|
18
17
|
from .address import ALL_DEV_ADDR, HGI_DEV_ADDR, NON_DEV_ADDR
|
|
@@ -120,7 +119,7 @@ class Engine:
|
|
|
120
119
|
self._log_all_mqtt = kwargs.pop(SZ_LOG_ALL_MQTT, False)
|
|
121
120
|
self._kwargs: dict[str, Any] = kwargs # HACK
|
|
122
121
|
|
|
123
|
-
self._engine_lock = Lock()
|
|
122
|
+
self._engine_lock = asyncio.Lock()
|
|
124
123
|
self._engine_state: (
|
|
125
124
|
tuple[_MsgHandlerT | None, bool | None, *tuple[Any, ...]] | None
|
|
126
125
|
) = None
|
|
@@ -217,15 +216,13 @@ class Engine:
|
|
|
217
216
|
async def stop(self) -> None:
|
|
218
217
|
"""Close the transport (will stop the protocol)."""
|
|
219
218
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
await asyncio.gather(*tasks)
|
|
225
|
-
except asyncio.CancelledError:
|
|
226
|
-
pass
|
|
219
|
+
# Shutdown Safety - wait for tasks to clean up
|
|
220
|
+
tasks = [t for t in self._tasks if not t.done()]
|
|
221
|
+
for t in tasks:
|
|
222
|
+
t.cancel()
|
|
227
223
|
|
|
228
|
-
|
|
224
|
+
if tasks:
|
|
225
|
+
await asyncio.wait(tasks)
|
|
229
226
|
|
|
230
227
|
if self._transport:
|
|
231
228
|
self._transport.close()
|
|
@@ -233,12 +230,14 @@ class Engine:
|
|
|
233
230
|
|
|
234
231
|
return None
|
|
235
232
|
|
|
236
|
-
def _pause(self, *args: Any) -> None:
|
|
233
|
+
async def _pause(self, *args: Any) -> None:
|
|
237
234
|
"""Pause the (active) engine or raise a RuntimeError."""
|
|
238
|
-
|
|
239
|
-
if
|
|
235
|
+
# Async lock handling
|
|
236
|
+
if self._engine_lock.locked():
|
|
240
237
|
raise RuntimeError("Unable to pause engine, failed to acquire lock")
|
|
241
238
|
|
|
239
|
+
await self._engine_lock.acquire()
|
|
240
|
+
|
|
242
241
|
if self._engine_state is not None:
|
|
243
242
|
self._engine_lock.release()
|
|
244
243
|
raise RuntimeError("Unable to pause engine, it is already paused")
|
|
@@ -255,13 +254,18 @@ class Engine:
|
|
|
255
254
|
|
|
256
255
|
self._engine_state = (handler, read_only, *args)
|
|
257
256
|
|
|
258
|
-
def _resume(self) -> tuple[Any]: # FIXME: not atomic
|
|
257
|
+
async def _resume(self) -> tuple[Any]: # FIXME: not atomic
|
|
259
258
|
"""Resume the (paused) engine or raise a RuntimeError."""
|
|
260
259
|
|
|
261
260
|
args: tuple[Any] # mypy
|
|
262
261
|
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
# Async lock with timeout
|
|
263
|
+
try:
|
|
264
|
+
await asyncio.wait_for(self._engine_lock.acquire(), timeout=0.1)
|
|
265
|
+
except TimeoutError as err:
|
|
266
|
+
raise RuntimeError(
|
|
267
|
+
"Unable to resume engine, failed to acquire lock"
|
|
268
|
+
) from err
|
|
265
269
|
|
|
266
270
|
if self._engine_state is None:
|
|
267
271
|
self._engine_lock.release()
|
ramses_tx/transport.py
CHANGED
|
@@ -49,7 +49,6 @@ import logging
|
|
|
49
49
|
import os
|
|
50
50
|
import re
|
|
51
51
|
import sys
|
|
52
|
-
import time
|
|
53
52
|
from collections import deque
|
|
54
53
|
from collections.abc import Awaitable, Callable, Iterable
|
|
55
54
|
from datetime import datetime as dt, timedelta as td
|
|
@@ -979,18 +978,21 @@ class FileTransport(_ReadTransport, _FileTransportAbstractor):
|
|
|
979
978
|
if bool(disable_sending) is False:
|
|
980
979
|
raise exc.TransportSourceInvalid("This Transport cannot send packets")
|
|
981
980
|
|
|
981
|
+
self._evt_reading = asyncio.Event()
|
|
982
|
+
|
|
982
983
|
self._extra[SZ_READER_TASK] = self._reader_task = self._loop.create_task(
|
|
983
984
|
self._start_reader(), name="FileTransport._start_reader()"
|
|
984
985
|
)
|
|
985
986
|
|
|
986
987
|
self._make_connection(None)
|
|
987
988
|
|
|
988
|
-
async def _start_reader(self) -> None:
|
|
989
|
+
async def _start_reader(self) -> None:
|
|
989
990
|
"""Start the reader task."""
|
|
990
991
|
self._reading = True
|
|
992
|
+
self._evt_reading.set() # Start in reading state
|
|
993
|
+
|
|
991
994
|
try:
|
|
992
|
-
|
|
993
|
-
await self.loop.run_in_executor(None, self._blocking_reader)
|
|
995
|
+
await self._producer_loop()
|
|
994
996
|
except Exception as err:
|
|
995
997
|
self.loop.call_soon_threadsafe(
|
|
996
998
|
functools.partial(self._protocol.connection_lost, err) # type: ignore[arg-type]
|
|
@@ -1000,47 +1002,59 @@ class FileTransport(_ReadTransport, _FileTransportAbstractor):
|
|
|
1000
1002
|
functools.partial(self._protocol.connection_lost, None)
|
|
1001
1003
|
)
|
|
1002
1004
|
|
|
1003
|
-
def
|
|
1005
|
+
def pause_reading(self) -> None:
|
|
1006
|
+
"""Pause the receiving end (no data to protocol.pkt_received())."""
|
|
1007
|
+
self._reading = False
|
|
1008
|
+
self._evt_reading.clear() # Puts the loop to sleep efficiently
|
|
1009
|
+
|
|
1010
|
+
def resume_reading(self) -> None:
|
|
1011
|
+
"""Resume the receiving end."""
|
|
1012
|
+
self._reading = True
|
|
1013
|
+
self._evt_reading.set() # Wakes the loop immediately
|
|
1014
|
+
|
|
1015
|
+
async def _producer_loop(self) -> None:
|
|
1004
1016
|
"""Loop through the packet source for Frames and process them."""
|
|
1017
|
+
# NOTE: fileinput interaction remains synchronous-blocking for simplicity,
|
|
1018
|
+
# but the PAUSE mechanism is now async-non-blocking.
|
|
1005
1019
|
|
|
1006
1020
|
if isinstance(self._pkt_source, dict):
|
|
1007
1021
|
for dtm_str, pkt_line in self._pkt_source.items(): # assume dtm_str is OK
|
|
1008
|
-
self._process_line(dtm_str, pkt_line)
|
|
1022
|
+
await self._process_line(dtm_str, pkt_line)
|
|
1009
1023
|
|
|
1010
1024
|
elif isinstance(self._pkt_source, str): # file_name, used in client parse
|
|
1011
1025
|
# open file file_name before reading
|
|
1012
1026
|
try:
|
|
1013
1027
|
with fileinput.input(files=self._pkt_source, encoding="utf-8") as file:
|
|
1014
1028
|
for dtm_pkt_line in file: # self._pkt_source:
|
|
1015
|
-
self._process_line_from_raw(dtm_pkt_line)
|
|
1029
|
+
await self._process_line_from_raw(dtm_pkt_line)
|
|
1016
1030
|
except FileNotFoundError as err:
|
|
1017
1031
|
_LOGGER.warning(f"Correct the packet file name; {err}")
|
|
1018
1032
|
|
|
1019
1033
|
elif isinstance(self._pkt_source, TextIOWrapper): # used by client monitor
|
|
1020
1034
|
for dtm_pkt_line in self._pkt_source: # should check dtm_str is OK
|
|
1021
|
-
self._process_line_from_raw(dtm_pkt_line)
|
|
1035
|
+
await self._process_line_from_raw(dtm_pkt_line)
|
|
1022
1036
|
|
|
1023
1037
|
else:
|
|
1024
1038
|
raise exc.TransportSourceInvalid(
|
|
1025
1039
|
f"Packet source is not dict, TextIOWrapper or str: {self._pkt_source:!r}"
|
|
1026
1040
|
)
|
|
1027
1041
|
|
|
1028
|
-
def _process_line_from_raw(self, line: str) -> None:
|
|
1042
|
+
async def _process_line_from_raw(self, line: str) -> None:
|
|
1029
1043
|
"""Helper to process raw lines."""
|
|
1030
1044
|
# there may be blank lines in annotated log files
|
|
1031
1045
|
if (line := line.strip()) and line[:1] != "#":
|
|
1032
|
-
self._process_line(line[:26], line[27:])
|
|
1046
|
+
await self._process_line(line[:26], line[27:])
|
|
1033
1047
|
# this is where the parsing magic happens!
|
|
1034
1048
|
|
|
1035
|
-
def _process_line(self, dtm_str: str, frame: str) -> None:
|
|
1049
|
+
async def _process_line(self, dtm_str: str, frame: str) -> None:
|
|
1036
1050
|
"""Push frame to protocol in a thread-safe way."""
|
|
1037
|
-
while
|
|
1038
|
-
|
|
1051
|
+
# Efficient wait - 0% CPU usage while paused
|
|
1052
|
+
await self._evt_reading.wait()
|
|
1039
1053
|
|
|
1040
1054
|
self._frame_read(dtm_str, frame)
|
|
1041
1055
|
|
|
1042
|
-
#
|
|
1043
|
-
|
|
1056
|
+
# Yield control to the event loop to prevent starvation during large file reads
|
|
1057
|
+
await asyncio.sleep(0)
|
|
1044
1058
|
|
|
1045
1059
|
def _close(self, exc: exc.RamsesException | None = None) -> None:
|
|
1046
1060
|
"""Close the transport (cancel any outstanding tasks).
|
ramses_tx/version.py
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|