syndesi 0.3.2__py3-none-any.whl → 0.4.1__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.
- syndesi/adapters/adapter.py +126 -165
- syndesi/adapters/auto.py +1 -1
- syndesi/adapters/backend/adapter_backend.py +96 -63
- syndesi/adapters/backend/adapter_session.py +53 -153
- syndesi/adapters/backend/descriptors.py +3 -2
- syndesi/adapters/backend/ip_backend.py +1 -0
- syndesi/adapters/backend/serialport_backend.py +19 -13
- syndesi/adapters/backend/stop_condition_backend.py +99 -243
- syndesi/adapters/backend/visa_backend.py +7 -7
- syndesi/adapters/ip.py +6 -10
- syndesi/adapters/serialport.py +4 -5
- syndesi/adapters/stop_condition.py +47 -26
- syndesi/adapters/timeout.py +2 -2
- syndesi/adapters/visa.py +2 -2
- syndesi/cli/shell_tools.py +95 -97
- syndesi/protocols/delimited.py +16 -21
- syndesi/protocols/modbus.py +17 -14
- syndesi/protocols/raw.py +20 -16
- syndesi/protocols/scpi.py +17 -15
- syndesi/tools/backend_api.py +15 -16
- syndesi/tools/errors.py +28 -0
- syndesi/version.py +1 -1
- {syndesi-0.3.2.dist-info → syndesi-0.4.1.dist-info}/METADATA +2 -1
- {syndesi-0.3.2.dist-info → syndesi-0.4.1.dist-info}/RECORD +28 -28
- {syndesi-0.3.2.dist-info → syndesi-0.4.1.dist-info}/WHEEL +0 -0
- {syndesi-0.3.2.dist-info → syndesi-0.4.1.dist-info}/entry_points.txt +0 -0
- {syndesi-0.3.2.dist-info → syndesi-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {syndesi-0.3.2.dist-info → syndesi-0.4.1.dist-info}/top_level.txt +0 -0
syndesi/adapters/adapter.py
CHANGED
|
@@ -22,13 +22,12 @@ import subprocess
|
|
|
22
22
|
import sys
|
|
23
23
|
import threading
|
|
24
24
|
import time
|
|
25
|
-
import uuid
|
|
26
25
|
import weakref
|
|
27
26
|
from abc import ABC, abstractmethod
|
|
28
27
|
from collections.abc import Callable
|
|
29
28
|
from multiprocessing.connection import Client, Connection
|
|
30
29
|
from types import EllipsisType
|
|
31
|
-
from typing import Any
|
|
30
|
+
from typing import Any
|
|
32
31
|
import os
|
|
33
32
|
|
|
34
33
|
from .backend.backend_tools import BACKEND_REQUEST_DEFAULT_TIMEOUT
|
|
@@ -38,72 +37,51 @@ from ..tools.backend_api import (
|
|
|
38
37
|
BACKEND_PORT,
|
|
39
38
|
Action,
|
|
40
39
|
BackendResponse,
|
|
40
|
+
Fragment,
|
|
41
41
|
default_host,
|
|
42
|
-
is_event,
|
|
43
42
|
raise_if_error,
|
|
43
|
+
EXTRA_BUFFER_RESPONSE_TIME
|
|
44
44
|
)
|
|
45
45
|
from ..tools.log_settings import LoggerAlias
|
|
46
46
|
from .backend.adapter_backend import (
|
|
47
47
|
AdapterDisconnected,
|
|
48
|
-
|
|
48
|
+
AdapterResponseTimeout,
|
|
49
49
|
AdapterReadPayload,
|
|
50
50
|
AdapterSignal,
|
|
51
51
|
)
|
|
52
52
|
from .backend.descriptors import Descriptor
|
|
53
|
-
from .stop_condition import StopCondition,
|
|
53
|
+
from .stop_condition import StopCondition, Continuation, StopConditionType, Total
|
|
54
54
|
from .timeout import Timeout, TimeoutAction, any_to_timeout
|
|
55
55
|
|
|
56
|
-
DEFAULT_STOP_CONDITION =
|
|
56
|
+
DEFAULT_STOP_CONDITION = Continuation(time=0.1)
|
|
57
57
|
|
|
58
58
|
DEFAULT_TIMEOUT = Timeout(response=5, action='error')
|
|
59
59
|
|
|
60
|
-
SHUTDOWN_DELAY = 2
|
|
61
|
-
|
|
62
60
|
# Maximum time to let the backend start
|
|
63
61
|
START_TIMEOUT = 2
|
|
62
|
+
# Time to shutdown the backend
|
|
63
|
+
SHUTDOWN_DELAY = 2
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
# DATA_READY = auto()
|
|
70
|
-
# ADAPTER_DISCONNECTED = auto()
|
|
71
|
-
|
|
72
|
-
import time
|
|
73
|
-
import queue
|
|
74
|
-
from typing import TypeVar, Generic
|
|
75
|
-
|
|
76
|
-
T = TypeVar("T")
|
|
65
|
+
class SignalQueue(queue.Queue[AdapterSignal]):
|
|
66
|
+
def __init__(self) -> None:
|
|
67
|
+
self._read_payload_counter = 0
|
|
68
|
+
super().__init__(0)
|
|
77
69
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"""
|
|
81
|
-
Return (without removing) the head item.
|
|
70
|
+
def has_read_payload(self) -> bool:
|
|
71
|
+
return self._read_payload_counter > 0
|
|
82
72
|
|
|
83
|
-
Args:
|
|
84
|
-
block: If False, raise queue.Empty immediately if empty.
|
|
85
|
-
timeout: Max seconds to wait if block=True. None means wait forever.
|
|
86
73
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if not self._qsize():
|
|
93
|
-
raise queue.Empty
|
|
94
|
-
return self.queue[0] # type: ignore[attr-defined]
|
|
95
|
-
|
|
96
|
-
end = None if timeout is None else time.monotonic() + timeout
|
|
97
|
-
while not self._qsize():
|
|
98
|
-
if timeout is None:
|
|
99
|
-
self.not_empty.wait()
|
|
100
|
-
else:
|
|
101
|
-
remaining = end - time.monotonic()
|
|
102
|
-
if remaining <= 0:
|
|
103
|
-
raise queue.Empty
|
|
104
|
-
self.not_empty.wait(remaining)
|
|
74
|
+
def put(self, signal: AdapterSignal, block: bool = True, timeout: float | None = None) -> None:
|
|
75
|
+
if isinstance(signal, AdapterReadPayload):
|
|
76
|
+
self._read_payload_counter += 1
|
|
77
|
+
return super().put(signal, block, timeout)
|
|
78
|
+
|
|
105
79
|
|
|
106
|
-
|
|
80
|
+
def get(self, block: bool = True, timeout: float | None = None) -> AdapterSignal:
|
|
81
|
+
signal = super().get(block, timeout)
|
|
82
|
+
if isinstance(signal, AdapterReadPayload):
|
|
83
|
+
self._read_payload_counter -= 1
|
|
84
|
+
return signal
|
|
107
85
|
|
|
108
86
|
|
|
109
87
|
def is_backend_running(address: str, port: int) -> bool:
|
|
@@ -128,29 +106,23 @@ def start_backend(port: int | None = None) -> None:
|
|
|
128
106
|
str(BACKEND_PORT if port is None else port),
|
|
129
107
|
]
|
|
130
108
|
|
|
131
|
-
# Always sever stdio — if you leave any of these inherited,
|
|
132
|
-
# you can keep an implicit console/TTY attachment.
|
|
133
109
|
stdin = subprocess.DEVNULL
|
|
134
110
|
stdout = subprocess.DEVNULL
|
|
135
111
|
stderr = subprocess.DEVNULL
|
|
136
112
|
|
|
137
113
|
if os.name == "posix":
|
|
138
|
-
# New session == new process group, no longer the terminal's foreground PG.
|
|
139
|
-
# This prevents keyboard SIGINT/SIGTSTP from the parent's TTY.
|
|
140
114
|
subprocess.Popen(
|
|
141
115
|
arguments,
|
|
142
|
-
# cwd=None,
|
|
143
|
-
# env=None,
|
|
144
116
|
stdin=stdin,
|
|
145
117
|
stdout=stdout,
|
|
146
118
|
stderr=stderr,
|
|
147
|
-
start_new_session=True,
|
|
148
|
-
close_fds=True,
|
|
119
|
+
start_new_session=True,
|
|
120
|
+
close_fds=True,
|
|
149
121
|
)
|
|
150
122
|
|
|
151
123
|
else:
|
|
152
124
|
# Windows: detach from the parent's console so keyboard Ctrl+C won't propagate.
|
|
153
|
-
CREATE_NEW_PROCESS_GROUP = subprocess.CREATE_NEW_PROCESS_GROUP
|
|
125
|
+
CREATE_NEW_PROCESS_GROUP = subprocess.CREATE_NEW_PROCESS_GROUP # type: ignore
|
|
154
126
|
DETACHED_PROCESS = 0x00000008 # not exposed by subprocess on all Pythons
|
|
155
127
|
# Optional: CREATE_NO_WINDOW (no window even for console apps)
|
|
156
128
|
CREATE_NO_WINDOW = 0x08000000
|
|
@@ -159,13 +131,11 @@ def start_backend(port: int | None = None) -> None:
|
|
|
159
131
|
|
|
160
132
|
subprocess.Popen(
|
|
161
133
|
arguments,
|
|
162
|
-
# cwd=cwd,
|
|
163
|
-
# env=env,
|
|
164
134
|
stdin=stdin,
|
|
165
135
|
stdout=stdout,
|
|
166
136
|
stderr=stderr,
|
|
167
137
|
creationflags=creationflags,
|
|
168
|
-
close_fds=True,
|
|
138
|
+
close_fds=True,
|
|
169
139
|
)
|
|
170
140
|
|
|
171
141
|
class ReadScope(Enum):
|
|
@@ -177,7 +147,7 @@ class Adapter(ABC):
|
|
|
177
147
|
self,
|
|
178
148
|
descriptor: Descriptor,
|
|
179
149
|
alias: str = "",
|
|
180
|
-
stop_conditions: StopCondition | EllipsisType | list = ...,
|
|
150
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
181
151
|
timeout: Timeout | EllipsisType | NumberLike | None = ...,
|
|
182
152
|
encoding: str = "utf-8",
|
|
183
153
|
event_callback: Callable[[AdapterSignal], None] | None = None,
|
|
@@ -203,7 +173,7 @@ class Adapter(ABC):
|
|
|
203
173
|
super().__init__()
|
|
204
174
|
self._logger = logging.getLogger(LoggerAlias.ADAPTER.value)
|
|
205
175
|
self.encoding = encoding
|
|
206
|
-
self.
|
|
176
|
+
self._signal_queue: SignalQueue = SignalQueue()
|
|
207
177
|
self.event_callback: Callable[[AdapterSignal], None] | None = event_callback
|
|
208
178
|
self.backend_connection: Connection | None = None
|
|
209
179
|
self._backend_connection_lock = threading.Lock()
|
|
@@ -211,7 +181,6 @@ class Adapter(ABC):
|
|
|
211
181
|
self._make_backend_request_flag = threading.Event()
|
|
212
182
|
self.opened = False
|
|
213
183
|
self._alias = alias
|
|
214
|
-
self._read_buffer = []
|
|
215
184
|
|
|
216
185
|
if backend_address is None:
|
|
217
186
|
self._backend_address = default_host
|
|
@@ -235,10 +204,10 @@ class Adapter(ABC):
|
|
|
235
204
|
self.auto_open = auto_open
|
|
236
205
|
|
|
237
206
|
# Set the stop-condition
|
|
238
|
-
self._stop_conditions: list[StopCondition
|
|
207
|
+
self._stop_conditions: list[StopCondition]
|
|
239
208
|
if stop_conditions is ...:
|
|
240
209
|
self._default_stop_condition = True
|
|
241
|
-
self._stop_conditions = DEFAULT_STOP_CONDITION
|
|
210
|
+
self._stop_conditions = [DEFAULT_STOP_CONDITION]
|
|
242
211
|
else:
|
|
243
212
|
self._default_stop_condition = False
|
|
244
213
|
if isinstance(stop_conditions, StopCondition):
|
|
@@ -306,7 +275,7 @@ class Adapter(ABC):
|
|
|
306
275
|
raise RuntimeError("Failed to connect to backend") from err
|
|
307
276
|
self._read_thread = threading.Thread(
|
|
308
277
|
target=self.read_thread,
|
|
309
|
-
args=(self.
|
|
278
|
+
args=(self._signal_queue, self._make_backend_request_queue),
|
|
310
279
|
daemon=True,
|
|
311
280
|
)
|
|
312
281
|
self._read_thread.start()
|
|
@@ -348,7 +317,7 @@ class Adapter(ABC):
|
|
|
348
317
|
|
|
349
318
|
def read_thread(
|
|
350
319
|
self,
|
|
351
|
-
|
|
320
|
+
signal_queue: SignalQueue,
|
|
352
321
|
request_queue: queue.Queue[BackendResponse],
|
|
353
322
|
) -> None:
|
|
354
323
|
while True:
|
|
@@ -357,7 +326,7 @@ class Adapter(ABC):
|
|
|
357
326
|
raise RuntimeError("Backend connection wasn't initialized")
|
|
358
327
|
response: tuple[Any, ...] = self.backend_connection.recv()
|
|
359
328
|
except (EOFError, TypeError, OSError):
|
|
360
|
-
|
|
329
|
+
signal_queue.put(AdapterDisconnected())
|
|
361
330
|
request_queue.put((Action.ERROR_BACKEND_DISCONNECTED,))
|
|
362
331
|
break
|
|
363
332
|
else:
|
|
@@ -365,13 +334,14 @@ class Adapter(ABC):
|
|
|
365
334
|
raise RuntimeError(f"Invalid response from backend : {response}")
|
|
366
335
|
action = Action(response[0])
|
|
367
336
|
|
|
368
|
-
if
|
|
337
|
+
if action == Action.ADAPTER_SIGNAL:
|
|
338
|
+
#if is_event(action):
|
|
369
339
|
if len(response) <= 1:
|
|
370
340
|
raise RuntimeError(f"Invalid event response : {response}")
|
|
341
|
+
signal: AdapterSignal = response[1]
|
|
371
342
|
if self.event_callback is not None:
|
|
372
|
-
signal: AdapterSignal = response[1]
|
|
373
343
|
self.event_callback(signal)
|
|
374
|
-
|
|
344
|
+
signal_queue.put(signal)
|
|
375
345
|
else:
|
|
376
346
|
request_queue.put(response)
|
|
377
347
|
|
|
@@ -401,7 +371,7 @@ class Adapter(ABC):
|
|
|
401
371
|
self._logger.debug(f"Setting default timeout to {default_timeout}")
|
|
402
372
|
self._timeout = default_timeout
|
|
403
373
|
|
|
404
|
-
def set_stop_conditions(self, stop_conditions: StopCondition | None | list) -> None:
|
|
374
|
+
def set_stop_conditions(self, stop_conditions: StopCondition | None | list[StopCondition]) -> None:
|
|
405
375
|
"""
|
|
406
376
|
Overwrite the stop-condition
|
|
407
377
|
|
|
@@ -416,11 +386,7 @@ class Adapter(ABC):
|
|
|
416
386
|
elif stop_conditions is None:
|
|
417
387
|
self._stop_conditions = []
|
|
418
388
|
|
|
419
|
-
|
|
420
|
-
# payload = None
|
|
421
|
-
# else:
|
|
422
|
-
# payload = self._stop_conditions.compose_json()
|
|
423
|
-
self._make_backend_request(Action.SET_STOP_CONDITION, self._stop_conditions)
|
|
389
|
+
self._make_backend_request(Action.SET_STOP_CONDITIONs, self._stop_conditions)
|
|
424
390
|
|
|
425
391
|
def set_default_stop_condition(self, stop_condition: StopCondition) -> None:
|
|
426
392
|
"""
|
|
@@ -442,7 +408,7 @@ class Adapter(ABC):
|
|
|
442
408
|
)
|
|
443
409
|
while True:
|
|
444
410
|
try:
|
|
445
|
-
self.
|
|
411
|
+
self._signal_queue.get(block=False)
|
|
446
412
|
except queue.Empty:
|
|
447
413
|
break
|
|
448
414
|
|
|
@@ -469,11 +435,11 @@ class Adapter(ABC):
|
|
|
469
435
|
"""
|
|
470
436
|
Stop communication with the device
|
|
471
437
|
"""
|
|
472
|
-
self._logger.debug("Closing adapter frontend")
|
|
473
|
-
self._make_backend_request(Action.CLOSE)
|
|
474
438
|
if force:
|
|
439
|
+
self._logger.debug("Closing adapter frontend")
|
|
440
|
+
else:
|
|
475
441
|
self._logger.debug("Force closing adapter backend")
|
|
476
|
-
|
|
442
|
+
self._make_backend_request(Action.CLOSE, force)
|
|
477
443
|
|
|
478
444
|
with self._backend_connection_lock:
|
|
479
445
|
if self.backend_connection is not None:
|
|
@@ -497,9 +463,9 @@ class Adapter(ABC):
|
|
|
497
463
|
def read_detailed(
|
|
498
464
|
self,
|
|
499
465
|
timeout: Timeout | EllipsisType | None = ...,
|
|
500
|
-
|
|
466
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
501
467
|
scope : str = ReadScope.BUFFERED.value,
|
|
502
|
-
) ->
|
|
468
|
+
) -> AdapterReadPayload:
|
|
503
469
|
"""
|
|
504
470
|
Read data from the device
|
|
505
471
|
|
|
@@ -517,110 +483,104 @@ class Adapter(ABC):
|
|
|
517
483
|
signal : AdapterReadPayload
|
|
518
484
|
"""
|
|
519
485
|
_scope = ReadScope(scope)
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
# If it doesn't match our criteria, we trash it
|
|
523
|
-
# When waiting for the backend payload, we wait +0.5s so make sure we received everything
|
|
524
|
-
# This 0.5s could be changed if we're local or not by the way
|
|
525
|
-
|
|
486
|
+
output_signal = None
|
|
487
|
+
read_timeout = None
|
|
526
488
|
|
|
527
|
-
# First, ask for the backend time, this is the official start of the read
|
|
528
|
-
backend_read_start_time = cast(NumberLike, self._make_backend_request(Action.GET_BACKEND_TIME)[0])
|
|
529
|
-
|
|
530
|
-
# If not timeout is specified, use the default one
|
|
531
489
|
if timeout is ...:
|
|
532
490
|
read_timeout = self._timeout
|
|
533
491
|
else:
|
|
534
492
|
read_timeout = any_to_timeout(timeout)
|
|
493
|
+
|
|
494
|
+
if read_timeout is None:
|
|
495
|
+
raise RuntimeError("Cannot read without setting a timeout")
|
|
496
|
+
|
|
497
|
+
if stop_conditions is not ...:
|
|
498
|
+
if isinstance(stop_conditions, StopCondition):
|
|
499
|
+
stop_conditions = [stop_conditions]
|
|
500
|
+
self._make_backend_request(Action.SET_STOP_CONDITIONs, stop_conditions)
|
|
501
|
+
|
|
502
|
+
# First, we check if data is in the buffer and if the scope if set to BUFFERED
|
|
503
|
+
while _scope == ReadScope.BUFFERED and self._signal_queue.has_read_payload():
|
|
504
|
+
signal = self._signal_queue.get()
|
|
505
|
+
if isinstance(signal, AdapterReadPayload):
|
|
506
|
+
output_signal = signal
|
|
507
|
+
break
|
|
508
|
+
# TODO : Implement disconnect ?
|
|
509
|
+
else:
|
|
510
|
+
# Nothing was found, ask the backend with a START_READ request. The backend will
|
|
511
|
+
# respond at most after the response_time with either data or a RESPONSE_TIMEOUT
|
|
535
512
|
|
|
536
|
-
if read_timeout is not None:
|
|
537
513
|
if not read_timeout.is_initialized():
|
|
538
514
|
raise RuntimeError("Timeout needs to be initialized")
|
|
539
515
|
|
|
540
|
-
|
|
541
|
-
# Calculate the queue timeout (time for a response + small delay)
|
|
542
|
-
#last_valid_timestamp = None
|
|
543
|
-
queue_timeout_timestamp = None
|
|
544
|
-
if read_timeout is not None:
|
|
545
|
-
response_delay = read_timeout.response()
|
|
546
|
-
else:
|
|
547
|
-
response_delay = None
|
|
548
|
-
|
|
549
|
-
if response_delay is not None:
|
|
550
|
-
#last_valid_timestamp = backend_read_start_time + response_delay
|
|
551
|
-
queue_timeout_timestamp = time.time() + response_delay + BACKEND_REQUEST_DEFAULT_TIMEOUT
|
|
516
|
+
_response = read_timeout.response()
|
|
552
517
|
|
|
553
|
-
|
|
518
|
+
read_init_time = time.time()
|
|
519
|
+
start_read_id = self._make_backend_request(Action.START_READ, _response)[0]
|
|
554
520
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
queue_timeout = None
|
|
521
|
+
if _response is None:
|
|
522
|
+
# Wait indefinitely
|
|
523
|
+
read_stop_timestamp = None
|
|
559
524
|
else:
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
queue_timeout = 0
|
|
525
|
+
# Wait for the response time + a bit more
|
|
526
|
+
read_stop_timestamp = read_init_time + _response
|
|
563
527
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
528
|
+
# else:
|
|
529
|
+
# start_read_id = None
|
|
530
|
+
# read_init_time = None
|
|
567
531
|
|
|
532
|
+
|
|
533
|
+
while True:
|
|
534
|
+
try:
|
|
535
|
+
if read_stop_timestamp is None:
|
|
536
|
+
queue_timeout = None
|
|
537
|
+
else:
|
|
538
|
+
queue_timeout = max(0, read_stop_timestamp - time.time() + EXTRA_BUFFER_RESPONSE_TIME)
|
|
539
|
+
|
|
540
|
+
signal = self._signal_queue.get(timeout=queue_timeout)
|
|
541
|
+
except queue.Empty:
|
|
542
|
+
raise RuntimeError('Failed to receive response from backend')
|
|
568
543
|
if isinstance(signal, AdapterReadPayload):
|
|
569
|
-
if response_delay is not None and signal.response_timestamp - backend_read_start_time > response_delay:
|
|
570
|
-
# This signal happened after the max response time, act as if a timeout occured
|
|
571
|
-
# and do not pop it out of the queue
|
|
572
|
-
# TODO : Make _timeout always Timeout, never None ?
|
|
573
|
-
output_signal = None
|
|
574
|
-
break
|
|
575
|
-
|
|
576
|
-
if _scope == ReadScope.NEXT and signal.response_timestamp < backend_read_start_time:
|
|
577
|
-
# The payload happened before the read start
|
|
578
|
-
self._event_queue.get()
|
|
579
|
-
continue
|
|
580
|
-
|
|
581
|
-
if response_delay is not None:
|
|
582
|
-
if signal.response_timestamp - backend_read_start_time > response_delay:
|
|
583
|
-
self._event_queue.get()
|
|
584
|
-
output_signal = None
|
|
585
|
-
break
|
|
586
|
-
|
|
587
|
-
# Other wise the payload is valid
|
|
588
|
-
self._event_queue.get()
|
|
589
544
|
output_signal = signal
|
|
590
545
|
break
|
|
591
|
-
|
|
592
546
|
elif isinstance(signal, AdapterDisconnected):
|
|
593
|
-
self._event_queue.get()
|
|
594
547
|
raise RuntimeError("Adapter disconnected")
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
548
|
+
elif isinstance(signal, AdapterResponseTimeout):
|
|
549
|
+
if start_read_id == signal.identifier:
|
|
550
|
+
output_signal = None
|
|
551
|
+
break
|
|
552
|
+
# Otherwise ignore it
|
|
601
553
|
|
|
602
554
|
if output_signal is None:
|
|
603
|
-
# TODO : Make
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
555
|
+
# TODO : Make read_timeout always Timeout, never None ?
|
|
556
|
+
match read_timeout.action:
|
|
557
|
+
case TimeoutAction.RETURN_EMPTY:
|
|
558
|
+
t = time.time()
|
|
559
|
+
return AdapterReadPayload(
|
|
560
|
+
fragments=[Fragment(b'', t)],
|
|
561
|
+
stop_timestamp=t,
|
|
562
|
+
stop_condition_type=StopConditionType.TIMEOUT,
|
|
563
|
+
previous_read_buffer_used=False,
|
|
564
|
+
response_timestamp=None,
|
|
565
|
+
response_delay=None
|
|
566
|
+
)
|
|
567
|
+
case TimeoutAction.ERROR:
|
|
568
|
+
raise TimeoutError(
|
|
569
|
+
f"No response received from device within {read_timeout.response()} seconds"
|
|
570
|
+
)
|
|
571
|
+
case _:
|
|
572
|
+
raise NotImplementedError()
|
|
615
573
|
|
|
616
|
-
|
|
574
|
+
else:
|
|
575
|
+
return output_signal
|
|
617
576
|
|
|
618
577
|
def read(
|
|
619
578
|
self,
|
|
620
579
|
timeout: Timeout | EllipsisType | None = ...,
|
|
621
|
-
|
|
580
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
622
581
|
) -> bytes:
|
|
623
|
-
|
|
582
|
+
signal = self.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
|
|
583
|
+
return signal.data()
|
|
624
584
|
|
|
625
585
|
def _cleanup(self) -> None:
|
|
626
586
|
if self._init_ok and self.opened:
|
|
@@ -630,8 +590,8 @@ class Adapter(ABC):
|
|
|
630
590
|
self,
|
|
631
591
|
data: bytes | str,
|
|
632
592
|
timeout: Timeout | EllipsisType | None = ...,
|
|
633
|
-
|
|
634
|
-
) ->
|
|
593
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
594
|
+
) -> AdapterReadPayload:
|
|
635
595
|
"""
|
|
636
596
|
Shortcut function that combines
|
|
637
597
|
- flush_read
|
|
@@ -640,17 +600,18 @@ class Adapter(ABC):
|
|
|
640
600
|
"""
|
|
641
601
|
self.flushRead()
|
|
642
602
|
self.write(data)
|
|
643
|
-
return self.read_detailed(timeout=timeout,
|
|
603
|
+
return self.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
|
|
644
604
|
|
|
645
605
|
def query(
|
|
646
606
|
self,
|
|
647
607
|
data: bytes | str,
|
|
648
608
|
timeout: Timeout | EllipsisType | None = ...,
|
|
649
|
-
|
|
609
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
650
610
|
) -> bytes:
|
|
651
|
-
|
|
652
|
-
data=data, timeout=timeout,
|
|
653
|
-
)
|
|
611
|
+
signal = self.query_detailed(
|
|
612
|
+
data=data, timeout=timeout, stop_conditions=stop_conditions
|
|
613
|
+
)
|
|
614
|
+
return signal.data()
|
|
654
615
|
|
|
655
616
|
def set_event_callback(self, callback: Callable[[AdapterSignal], None]) -> None:
|
|
656
617
|
self.event_callback = callback
|
syndesi/adapters/auto.py
CHANGED
|
@@ -37,7 +37,7 @@ def auto_adapter(adapter_or_string: Adapter | str) -> Adapter:
|
|
|
37
37
|
return IP(
|
|
38
38
|
address=descriptor.address,
|
|
39
39
|
port=descriptor.port,
|
|
40
|
-
transport=descriptor.transport,
|
|
40
|
+
transport=descriptor.transport.value,
|
|
41
41
|
)
|
|
42
42
|
elif isinstance(descriptor, SerialPortDescriptor):
|
|
43
43
|
return SerialPort(port=descriptor.port, baudrate=descriptor.baudrate)
|