syndesi 0.4.1__py3-none-any.whl → 0.4.2__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 +48 -44
- syndesi/adapters/backend/adapter_backend.py +54 -36
- syndesi/adapters/backend/adapter_session.py +24 -25
- syndesi/adapters/backend/backend_status.py +0 -0
- syndesi/adapters/backend/backend_tools.py +1 -1
- syndesi/adapters/backend/descriptors.py +1 -1
- syndesi/adapters/backend/ip_backend.py +1 -1
- syndesi/adapters/backend/serialport_backend.py +6 -7
- syndesi/adapters/backend/stop_condition_backend.py +47 -26
- syndesi/adapters/backend/visa_backend.py +2 -2
- syndesi/adapters/ip.py +3 -3
- syndesi/adapters/stop_condition.py +10 -83
- syndesi/adapters/timeout.py +2 -29
- syndesi/cli/backend_status.py +7 -9
- syndesi/cli/console.py +1 -54
- syndesi/cli/shell.py +1 -14
- syndesi/cli/shell_tools.py +0 -5
- syndesi/protocols/delimited.py +7 -22
- syndesi/protocols/modbus.py +3 -3
- syndesi/protocols/raw.py +2 -2
- syndesi/protocols/scpi.py +5 -4
- syndesi/scripts/syndesi.py +1 -3
- syndesi/tools/backend_api.py +4 -37
- syndesi/tools/backend_logger.py +0 -1
- syndesi/tools/errors.py +4 -5
- syndesi/tools/log.py +0 -88
- syndesi/tools/types.py +0 -44
- syndesi/version.py +1 -1
- syndesi-0.4.2.dist-info/METADATA +96 -0
- syndesi-0.4.2.dist-info/RECORD +60 -0
- syndesi-0.4.1.dist-info/METADATA +0 -123
- syndesi-0.4.1.dist-info/RECORD +0 -59
- {syndesi-0.4.1.dist-info → syndesi-0.4.2.dist-info}/WHEEL +0 -0
- {syndesi-0.4.1.dist-info → syndesi-0.4.2.dist-info}/entry_points.txt +0 -0
- {syndesi-0.4.1.dist-info → syndesi-0.4.2.dist-info}/licenses/LICENSE +0 -0
- {syndesi-0.4.1.dist-info → syndesi-0.4.2.dist-info}/top_level.txt +0 -0
syndesi/adapters/adapter.py
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
# An adapter is meant to work with bytes objects but it can accept strings.
|
|
16
16
|
# Strings will automatically be converted to bytes using utf-8 encoding
|
|
17
17
|
|
|
18
|
-
from enum import Enum
|
|
19
18
|
import logging
|
|
19
|
+
import os
|
|
20
20
|
import queue
|
|
21
21
|
import subprocess
|
|
22
22
|
import sys
|
|
@@ -25,43 +25,44 @@ import time
|
|
|
25
25
|
import weakref
|
|
26
26
|
from abc import ABC, abstractmethod
|
|
27
27
|
from collections.abc import Callable
|
|
28
|
+
from enum import Enum
|
|
28
29
|
from multiprocessing.connection import Client, Connection
|
|
29
30
|
from types import EllipsisType
|
|
30
31
|
from typing import Any
|
|
31
|
-
import os
|
|
32
32
|
|
|
33
|
-
from .backend.backend_tools import BACKEND_REQUEST_DEFAULT_TIMEOUT
|
|
34
33
|
from syndesi.tools.types import NumberLike, is_number
|
|
35
34
|
|
|
36
35
|
from ..tools.backend_api import (
|
|
37
36
|
BACKEND_PORT,
|
|
37
|
+
EXTRA_BUFFER_RESPONSE_TIME,
|
|
38
38
|
Action,
|
|
39
39
|
BackendResponse,
|
|
40
40
|
Fragment,
|
|
41
41
|
default_host,
|
|
42
42
|
raise_if_error,
|
|
43
|
-
EXTRA_BUFFER_RESPONSE_TIME
|
|
44
43
|
)
|
|
45
44
|
from ..tools.log_settings import LoggerAlias
|
|
46
45
|
from .backend.adapter_backend import (
|
|
47
46
|
AdapterDisconnected,
|
|
48
|
-
AdapterResponseTimeout,
|
|
49
47
|
AdapterReadPayload,
|
|
48
|
+
AdapterResponseTimeout,
|
|
50
49
|
AdapterSignal,
|
|
51
50
|
)
|
|
51
|
+
from .backend.backend_tools import BACKEND_REQUEST_DEFAULT_TIMEOUT
|
|
52
52
|
from .backend.descriptors import Descriptor
|
|
53
|
-
from .stop_condition import
|
|
53
|
+
from .stop_condition import Continuation, StopCondition, StopConditionType
|
|
54
54
|
from .timeout import Timeout, TimeoutAction, any_to_timeout
|
|
55
55
|
|
|
56
56
|
DEFAULT_STOP_CONDITION = Continuation(time=0.1)
|
|
57
57
|
|
|
58
|
-
DEFAULT_TIMEOUT = Timeout(response=5, action=
|
|
58
|
+
DEFAULT_TIMEOUT = Timeout(response=5, action="error")
|
|
59
59
|
|
|
60
60
|
# Maximum time to let the backend start
|
|
61
61
|
START_TIMEOUT = 2
|
|
62
62
|
# Time to shutdown the backend
|
|
63
63
|
SHUTDOWN_DELAY = 2
|
|
64
64
|
|
|
65
|
+
|
|
65
66
|
class SignalQueue(queue.Queue[AdapterSignal]):
|
|
66
67
|
def __init__(self) -> None:
|
|
67
68
|
self._read_payload_counter = 0
|
|
@@ -70,12 +71,12 @@ class SignalQueue(queue.Queue[AdapterSignal]):
|
|
|
70
71
|
def has_read_payload(self) -> bool:
|
|
71
72
|
return self._read_payload_counter > 0
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
def put(
|
|
75
|
+
self, signal: AdapterSignal, block: bool = True, timeout: float | None = None
|
|
76
|
+
) -> None:
|
|
75
77
|
if isinstance(signal, AdapterReadPayload):
|
|
76
78
|
self._read_payload_counter += 1
|
|
77
79
|
return super().put(signal, block, timeout)
|
|
78
|
-
|
|
79
80
|
|
|
80
81
|
def get(self, block: bool = True, timeout: float | None = None) -> AdapterSignal:
|
|
81
82
|
signal = super().get(block, timeout)
|
|
@@ -94,19 +95,20 @@ def is_backend_running(address: str, port: int) -> bool:
|
|
|
94
95
|
conn.close()
|
|
95
96
|
return True
|
|
96
97
|
|
|
98
|
+
|
|
97
99
|
def start_backend(port: int | None = None) -> None:
|
|
98
100
|
arguments = [
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
stdin
|
|
101
|
+
sys.executable,
|
|
102
|
+
"-m",
|
|
103
|
+
"syndesi.adapters.backend.backend",
|
|
104
|
+
"-s",
|
|
105
|
+
str(SHUTDOWN_DELAY),
|
|
106
|
+
"-q",
|
|
107
|
+
"-p",
|
|
108
|
+
str(BACKEND_PORT if port is None else port),
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
stdin = subprocess.DEVNULL
|
|
110
112
|
stdout = subprocess.DEVNULL
|
|
111
113
|
stderr = subprocess.DEVNULL
|
|
112
114
|
|
|
@@ -122,8 +124,8 @@ def start_backend(port: int | None = None) -> None:
|
|
|
122
124
|
|
|
123
125
|
else:
|
|
124
126
|
# Windows: detach from the parent's console so keyboard Ctrl+C won't propagate.
|
|
125
|
-
CREATE_NEW_PROCESS_GROUP = subprocess.CREATE_NEW_PROCESS_GROUP
|
|
126
|
-
DETACHED_PROCESS = 0x00000008
|
|
127
|
+
CREATE_NEW_PROCESS_GROUP = subprocess.CREATE_NEW_PROCESS_GROUP # type: ignore
|
|
128
|
+
DETACHED_PROCESS = 0x00000008 # not exposed by subprocess on all Pythons
|
|
127
129
|
# Optional: CREATE_NO_WINDOW (no window even for console apps)
|
|
128
130
|
CREATE_NO_WINDOW = 0x08000000
|
|
129
131
|
|
|
@@ -138,9 +140,11 @@ def start_backend(port: int | None = None) -> None:
|
|
|
138
140
|
close_fds=True,
|
|
139
141
|
)
|
|
140
142
|
|
|
143
|
+
|
|
141
144
|
class ReadScope(Enum):
|
|
142
|
-
NEXT =
|
|
143
|
-
BUFFERED =
|
|
145
|
+
NEXT = "next"
|
|
146
|
+
BUFFERED = "buffered"
|
|
147
|
+
|
|
144
148
|
|
|
145
149
|
class Adapter(ABC):
|
|
146
150
|
def __init__(
|
|
@@ -215,7 +219,7 @@ class Adapter(ABC):
|
|
|
215
219
|
elif isinstance(stop_conditions, list):
|
|
216
220
|
self._stop_conditions = stop_conditions
|
|
217
221
|
else:
|
|
218
|
-
raise ValueError(
|
|
222
|
+
raise ValueError("Invalid stop_conditions")
|
|
219
223
|
|
|
220
224
|
# Set the timeout
|
|
221
225
|
self.is_default_timeout = False
|
|
@@ -335,7 +339,7 @@ class Adapter(ABC):
|
|
|
335
339
|
action = Action(response[0])
|
|
336
340
|
|
|
337
341
|
if action == Action.ADAPTER_SIGNAL:
|
|
338
|
-
|
|
342
|
+
# if is_event(action):
|
|
339
343
|
if len(response) <= 1:
|
|
340
344
|
raise RuntimeError(f"Invalid event response : {response}")
|
|
341
345
|
signal: AdapterSignal = response[1]
|
|
@@ -371,7 +375,9 @@ class Adapter(ABC):
|
|
|
371
375
|
self._logger.debug(f"Setting default timeout to {default_timeout}")
|
|
372
376
|
self._timeout = default_timeout
|
|
373
377
|
|
|
374
|
-
def set_stop_conditions(
|
|
378
|
+
def set_stop_conditions(
|
|
379
|
+
self, stop_conditions: StopCondition | None | list[StopCondition]
|
|
380
|
+
) -> None:
|
|
375
381
|
"""
|
|
376
382
|
Overwrite the stop-condition
|
|
377
383
|
|
|
@@ -411,7 +417,6 @@ class Adapter(ABC):
|
|
|
411
417
|
self._signal_queue.get(block=False)
|
|
412
418
|
except queue.Empty:
|
|
413
419
|
break
|
|
414
|
-
|
|
415
420
|
|
|
416
421
|
def previous_read_buffer_empty(self) -> bool:
|
|
417
422
|
"""
|
|
@@ -464,7 +469,7 @@ class Adapter(ABC):
|
|
|
464
469
|
self,
|
|
465
470
|
timeout: Timeout | EllipsisType | None = ...,
|
|
466
471
|
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
467
|
-
scope
|
|
472
|
+
scope: str = ReadScope.BUFFERED.value,
|
|
468
473
|
) -> AdapterReadPayload:
|
|
469
474
|
"""
|
|
470
475
|
Read data from the device
|
|
@@ -490,13 +495,13 @@ class Adapter(ABC):
|
|
|
490
495
|
read_timeout = self._timeout
|
|
491
496
|
else:
|
|
492
497
|
read_timeout = any_to_timeout(timeout)
|
|
493
|
-
|
|
498
|
+
|
|
494
499
|
if read_timeout is None:
|
|
495
500
|
raise RuntimeError("Cannot read without setting a timeout")
|
|
496
501
|
|
|
497
502
|
if stop_conditions is not ...:
|
|
498
503
|
if isinstance(stop_conditions, StopCondition):
|
|
499
|
-
stop_conditions = [stop_conditions]
|
|
504
|
+
stop_conditions = [stop_conditions]
|
|
500
505
|
self._make_backend_request(Action.SET_STOP_CONDITIONs, stop_conditions)
|
|
501
506
|
|
|
502
507
|
# First, we check if data is in the buffer and if the scope if set to BUFFERED
|
|
@@ -525,21 +530,21 @@ class Adapter(ABC):
|
|
|
525
530
|
# Wait for the response time + a bit more
|
|
526
531
|
read_stop_timestamp = read_init_time + _response
|
|
527
532
|
|
|
528
|
-
# else:
|
|
529
|
-
# start_read_id = None
|
|
530
|
-
# read_init_time = None
|
|
531
|
-
|
|
532
|
-
|
|
533
533
|
while True:
|
|
534
534
|
try:
|
|
535
535
|
if read_stop_timestamp is None:
|
|
536
536
|
queue_timeout = None
|
|
537
537
|
else:
|
|
538
|
-
queue_timeout = max(
|
|
538
|
+
queue_timeout = max(
|
|
539
|
+
0,
|
|
540
|
+
read_stop_timestamp
|
|
541
|
+
- time.time()
|
|
542
|
+
+ EXTRA_BUFFER_RESPONSE_TIME,
|
|
543
|
+
)
|
|
539
544
|
|
|
540
545
|
signal = self._signal_queue.get(timeout=queue_timeout)
|
|
541
|
-
except queue.Empty:
|
|
542
|
-
raise RuntimeError(
|
|
546
|
+
except queue.Empty as e:
|
|
547
|
+
raise RuntimeError("Failed to receive response from backend") from e
|
|
543
548
|
if isinstance(signal, AdapterReadPayload):
|
|
544
549
|
output_signal = signal
|
|
545
550
|
break
|
|
@@ -552,17 +557,16 @@ class Adapter(ABC):
|
|
|
552
557
|
# Otherwise ignore it
|
|
553
558
|
|
|
554
559
|
if output_signal is None:
|
|
555
|
-
# TODO : Make read_timeout always Timeout, never None ?
|
|
556
560
|
match read_timeout.action:
|
|
557
561
|
case TimeoutAction.RETURN_EMPTY:
|
|
558
562
|
t = time.time()
|
|
559
563
|
return AdapterReadPayload(
|
|
560
|
-
fragments=[Fragment(b
|
|
564
|
+
fragments=[Fragment(b"", t)],
|
|
561
565
|
stop_timestamp=t,
|
|
562
566
|
stop_condition_type=StopConditionType.TIMEOUT,
|
|
563
567
|
previous_read_buffer_used=False,
|
|
564
568
|
response_timestamp=None,
|
|
565
|
-
response_delay=None
|
|
569
|
+
response_delay=None,
|
|
566
570
|
)
|
|
567
571
|
case TimeoutAction.ERROR:
|
|
568
572
|
raise TimeoutError(
|
|
@@ -570,7 +574,7 @@ class Adapter(ABC):
|
|
|
570
574
|
)
|
|
571
575
|
case _:
|
|
572
576
|
raise NotImplementedError()
|
|
573
|
-
|
|
577
|
+
|
|
574
578
|
else:
|
|
575
579
|
return output_signal
|
|
576
580
|
|
|
@@ -7,19 +7,18 @@
|
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
9
|
import socket
|
|
10
|
+
import time
|
|
10
11
|
from abc import ABC, abstractmethod
|
|
11
12
|
from collections.abc import Generator
|
|
12
13
|
from dataclasses import dataclass
|
|
13
14
|
from enum import Enum
|
|
14
15
|
from multiprocessing.connection import Connection
|
|
15
16
|
from threading import Thread
|
|
16
|
-
import
|
|
17
|
-
from typing import Protocol, cast
|
|
18
|
-
|
|
19
|
-
from syndesi.tools.types import NumberLike
|
|
17
|
+
from typing import Protocol
|
|
20
18
|
|
|
21
19
|
from ...tools.backend_api import AdapterBackendStatus, Fragment
|
|
22
20
|
from ...tools.log_settings import LoggerAlias
|
|
21
|
+
from ..stop_condition import StopConditionType
|
|
23
22
|
from .descriptors import Descriptor
|
|
24
23
|
from .stop_condition_backend import (
|
|
25
24
|
ContinuationBackend,
|
|
@@ -27,8 +26,6 @@ from .stop_condition_backend import (
|
|
|
27
26
|
TotalBackend,
|
|
28
27
|
)
|
|
29
28
|
|
|
30
|
-
from ..stop_condition import Continuation, StopConditionType
|
|
31
|
-
|
|
32
29
|
|
|
33
30
|
class HasFileno(Protocol):
|
|
34
31
|
def fileno(self) -> int:
|
|
@@ -82,12 +79,13 @@ class AdapterDisconnected(AdapterSignal):
|
|
|
82
79
|
# def __repr__(self) -> str:
|
|
83
80
|
# return self.__str__()
|
|
84
81
|
|
|
82
|
+
|
|
85
83
|
@dataclass
|
|
86
84
|
class AdapterResponseTimeout(AdapterSignal):
|
|
87
|
-
identifier
|
|
85
|
+
identifier: int
|
|
88
86
|
|
|
89
87
|
def __str__(self) -> str:
|
|
90
|
-
return
|
|
88
|
+
return "Response timeout"
|
|
91
89
|
|
|
92
90
|
def __repr__(self) -> str:
|
|
93
91
|
return self.__str__()
|
|
@@ -99,9 +97,9 @@ class AdapterReadPayload(AdapterSignal):
|
|
|
99
97
|
stop_timestamp: float
|
|
100
98
|
stop_condition_type: StopConditionType
|
|
101
99
|
previous_read_buffer_used: bool
|
|
102
|
-
response_timestamp
|
|
100
|
+
response_timestamp: float | None
|
|
103
101
|
# Only used by client and set by frontend
|
|
104
|
-
response_delay
|
|
102
|
+
response_delay: float | None = None
|
|
105
103
|
|
|
106
104
|
def data(self) -> bytes:
|
|
107
105
|
return b"".join([f.data for f in self.fragments])
|
|
@@ -118,9 +116,10 @@ class AdapterReadPayload(AdapterSignal):
|
|
|
118
116
|
@dataclass
|
|
119
117
|
class ResponseRequest:
|
|
120
118
|
timestamp: float
|
|
121
|
-
identifier
|
|
119
|
+
identifier: int
|
|
120
|
+
|
|
122
121
|
|
|
123
|
-
def nmin(a
|
|
122
|
+
def nmin(a: float | None, b: float | None) -> float | None:
|
|
124
123
|
if a is None and b is None:
|
|
125
124
|
return None
|
|
126
125
|
elif a is None:
|
|
@@ -130,6 +129,7 @@ def nmin(a : float | None, b : float | None) -> float | None:
|
|
|
130
129
|
else:
|
|
131
130
|
return min(a, b)
|
|
132
131
|
|
|
132
|
+
|
|
133
133
|
class AdapterBackend(ABC):
|
|
134
134
|
class ThreadCommands(Enum):
|
|
135
135
|
STOP = b"0"
|
|
@@ -168,7 +168,7 @@ class AdapterBackend(ABC):
|
|
|
168
168
|
# None : No ask
|
|
169
169
|
# float : Ask for a response to happen at the specified value at max
|
|
170
170
|
self._response_request: ResponseRequest | None = None
|
|
171
|
-
self._response_request_start
|
|
171
|
+
self._response_request_start: float | None = None
|
|
172
172
|
|
|
173
173
|
self._first_fragment = True
|
|
174
174
|
|
|
@@ -246,10 +246,10 @@ class AdapterBackend(ABC):
|
|
|
246
246
|
@abstractmethod
|
|
247
247
|
def _socket_read(self) -> Fragment:
|
|
248
248
|
raise NotImplementedError
|
|
249
|
-
|
|
250
|
-
def _fragments_to_string(self, fragments
|
|
249
|
+
|
|
250
|
+
def _fragments_to_string(self, fragments: list[Fragment]) -> str:
|
|
251
251
|
if len(fragments) > 0:
|
|
252
|
-
return
|
|
252
|
+
return "+".join(repr(f.data) for f in fragments)
|
|
253
253
|
else:
|
|
254
254
|
return str([])
|
|
255
255
|
|
|
@@ -263,7 +263,10 @@ class AdapterBackend(ABC):
|
|
|
263
263
|
self.close()
|
|
264
264
|
yield AdapterDisconnected()
|
|
265
265
|
else:
|
|
266
|
-
self._logger.debug(
|
|
266
|
+
self._logger.debug(
|
|
267
|
+
f"New fragment {fragment_delta_t:+.3f} {fragment}"
|
|
268
|
+
+ (" (first)" if self._first_fragment else "")
|
|
269
|
+
)
|
|
267
270
|
if self._status == AdapterBackendStatus.CONNECTED:
|
|
268
271
|
t = time.time()
|
|
269
272
|
|
|
@@ -272,7 +275,9 @@ class AdapterBackend(ABC):
|
|
|
272
275
|
|
|
273
276
|
if self._response_request is not None:
|
|
274
277
|
for stop_condition in self._stop_conditions:
|
|
275
|
-
if isinstance(
|
|
278
|
+
if isinstance(
|
|
279
|
+
stop_condition, (ContinuationBackend, TotalBackend)
|
|
280
|
+
):
|
|
276
281
|
self._response_request = None
|
|
277
282
|
break
|
|
278
283
|
|
|
@@ -287,15 +292,18 @@ class AdapterBackend(ABC):
|
|
|
287
292
|
kept = fragment
|
|
288
293
|
|
|
289
294
|
# Run each stop condition one after the other, if a stop is reached, stop evaluating
|
|
290
|
-
stop_condition_type
|
|
295
|
+
stop_condition_type: StopConditionType
|
|
291
296
|
for stop_condition in self._stop_conditions:
|
|
292
|
-
|
|
293
|
-
|
|
297
|
+
(
|
|
298
|
+
stop,
|
|
299
|
+
kept,
|
|
300
|
+
self._previous_buffer,
|
|
301
|
+
self._next_timeout_timestamp,
|
|
302
|
+
) = stop_condition.evaluate(kept)
|
|
294
303
|
if stop:
|
|
295
304
|
stop_condition_type = stop_condition.type()
|
|
296
305
|
break
|
|
297
306
|
|
|
298
|
-
|
|
299
307
|
# if kept.data != b'':
|
|
300
308
|
# self.fragments.append(kept)
|
|
301
309
|
|
|
@@ -303,14 +311,22 @@ class AdapterBackend(ABC):
|
|
|
303
311
|
|
|
304
312
|
if stop:
|
|
305
313
|
self._first_fragment = True
|
|
306
|
-
self._logger.debug(
|
|
307
|
-
|
|
314
|
+
self._logger.debug(
|
|
315
|
+
f"Payload {self._fragments_to_string(self.fragments)} ({stop_condition_type.value})"
|
|
316
|
+
)
|
|
317
|
+
if (
|
|
318
|
+
self._response_request_start is None
|
|
319
|
+
or len(self.fragments) == 0
|
|
320
|
+
):
|
|
308
321
|
response_delay = None
|
|
309
322
|
else:
|
|
310
323
|
if self.fragments[0].timestamp is None:
|
|
311
324
|
response_delay = None
|
|
312
325
|
else:
|
|
313
|
-
response_delay =
|
|
326
|
+
response_delay = (
|
|
327
|
+
self.fragments[0].timestamp
|
|
328
|
+
- self._response_request_start
|
|
329
|
+
)
|
|
314
330
|
self._response_request_start = None
|
|
315
331
|
yield AdapterReadPayload(
|
|
316
332
|
fragments=self.fragments,
|
|
@@ -318,9 +334,9 @@ class AdapterBackend(ABC):
|
|
|
318
334
|
stop_condition_type=stop_condition_type,
|
|
319
335
|
previous_read_buffer_used=False,
|
|
320
336
|
response_timestamp=self.fragments[0].timestamp,
|
|
321
|
-
response_delay=response_delay
|
|
337
|
+
response_delay=response_delay,
|
|
322
338
|
)
|
|
323
|
-
self._next_timeout_timestamp = None
|
|
339
|
+
self._next_timeout_timestamp = None # Experiment !
|
|
324
340
|
self.fragments.clear()
|
|
325
341
|
|
|
326
342
|
if len(self._previous_buffer.data) > 0 and stop:
|
|
@@ -341,8 +357,9 @@ class AdapterBackend(ABC):
|
|
|
341
357
|
"""
|
|
342
358
|
self._response_request_start = time.time()
|
|
343
359
|
self._logger.debug(f"Setup read [{identifier}] in {response_time:.3f} s")
|
|
344
|
-
self._response_request = ResponseRequest(
|
|
345
|
-
|
|
360
|
+
self._response_request = ResponseRequest(
|
|
361
|
+
self._response_request_start + response_time, identifier
|
|
362
|
+
)
|
|
346
363
|
|
|
347
364
|
@abstractmethod
|
|
348
365
|
def is_opened(self) -> bool:
|
|
@@ -366,7 +383,9 @@ class AdapterBackend(ABC):
|
|
|
366
383
|
response_delay = None
|
|
367
384
|
else:
|
|
368
385
|
if self.fragments[0].timestamp is not None:
|
|
369
|
-
response_delay =
|
|
386
|
+
response_delay = (
|
|
387
|
+
self.fragments[0].timestamp - self._response_request_start
|
|
388
|
+
)
|
|
370
389
|
else:
|
|
371
390
|
response_delay = None
|
|
372
391
|
self._response_request_start = None
|
|
@@ -376,8 +395,10 @@ class AdapterBackend(ABC):
|
|
|
376
395
|
stop_condition_type=StopConditionType.TIMEOUT,
|
|
377
396
|
previous_read_buffer_used=False,
|
|
378
397
|
fragments=self.fragments,
|
|
379
|
-
response_timestamp=
|
|
380
|
-
|
|
398
|
+
response_timestamp=(
|
|
399
|
+
self.fragments[0].timestamp if len(self.fragments) > 0 else None
|
|
400
|
+
),
|
|
401
|
+
response_delay=response_delay,
|
|
381
402
|
)
|
|
382
403
|
# Reset response request
|
|
383
404
|
if self._response_request is not None:
|
|
@@ -388,8 +409,7 @@ class AdapterBackend(ABC):
|
|
|
388
409
|
return output
|
|
389
410
|
|
|
390
411
|
elif (
|
|
391
|
-
self._next_timeout_origin
|
|
392
|
-
== self.AdapterTimeoutEventOrigin.RESPONSE_REQUEST
|
|
412
|
+
self._next_timeout_origin == self.AdapterTimeoutEventOrigin.RESPONSE_REQUEST
|
|
393
413
|
):
|
|
394
414
|
if self._response_request is not None:
|
|
395
415
|
signal = AdapterResponseTimeout(self._response_request.identifier)
|
|
@@ -402,8 +422,6 @@ class AdapterBackend(ABC):
|
|
|
402
422
|
min_timestamp = None
|
|
403
423
|
self._next_timeout_origin = None
|
|
404
424
|
|
|
405
|
-
t = time.time()
|
|
406
|
-
|
|
407
425
|
if self._next_timeout_timestamp is not None:
|
|
408
426
|
min_timestamp = self._next_timeout_timestamp
|
|
409
427
|
self._next_timeout_origin = self.AdapterTimeoutEventOrigin.TIMEOUT
|
|
@@ -11,9 +11,11 @@ import threading
|
|
|
11
11
|
import time
|
|
12
12
|
from enum import Enum
|
|
13
13
|
from multiprocessing.connection import Pipe, wait
|
|
14
|
-
from typing import Any
|
|
14
|
+
from typing import Any
|
|
15
15
|
|
|
16
|
-
from syndesi.adapters.backend.stop_condition_backend import
|
|
16
|
+
from syndesi.adapters.backend.stop_condition_backend import (
|
|
17
|
+
stop_condition_to_backend,
|
|
18
|
+
)
|
|
17
19
|
from syndesi.tools.errors import make_error_description
|
|
18
20
|
from syndesi.tools.types import NumberLike
|
|
19
21
|
|
|
@@ -21,9 +23,6 @@ from ...tools.backend_api import Action, frontend_send
|
|
|
21
23
|
from ...tools.log_settings import LoggerAlias
|
|
22
24
|
from .adapter_backend import (
|
|
23
25
|
AdapterBackend,
|
|
24
|
-
AdapterDisconnected,
|
|
25
|
-
#AdapterReadInit,
|
|
26
|
-
AdapterReadPayload,
|
|
27
26
|
Selectable,
|
|
28
27
|
nmin,
|
|
29
28
|
)
|
|
@@ -37,15 +36,16 @@ from .descriptors import (
|
|
|
37
36
|
)
|
|
38
37
|
from .ip_backend import IPBackend
|
|
39
38
|
from .serialport_backend import SerialPortBackend
|
|
40
|
-
|
|
39
|
+
|
|
40
|
+
# from .stop_condition_backend import stop_condition_from_list
|
|
41
41
|
from .visa_backend import VisaBackend
|
|
42
|
-
from pathlib import Path
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
class TimeoutEvent(Enum):
|
|
46
45
|
MONITORING = 0
|
|
47
46
|
ADAPTER = 1
|
|
48
|
-
#CONNECTIONS = 2
|
|
47
|
+
# CONNECTIONS = 2
|
|
48
|
+
|
|
49
49
|
|
|
50
50
|
def get_adapter(descriptor: Descriptor) -> AdapterBackend:
|
|
51
51
|
# The adapter doesn't exist, create it
|
|
@@ -99,12 +99,10 @@ class AdapterSession(threading.Thread):
|
|
|
99
99
|
self._shutdown_counter_top = None
|
|
100
100
|
self._shutdown_counter = None
|
|
101
101
|
|
|
102
|
-
#self._timeout_events: list[tuple[TimeoutEvent, float]] = []
|
|
102
|
+
# self._timeout_events: list[tuple[TimeoutEvent, float]] = []
|
|
103
103
|
|
|
104
104
|
self._read_init_id = 0
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
106
|
def add_connection(self, conn: NamedConnection) -> None:
|
|
109
107
|
with self._connections_lock:
|
|
110
108
|
self.connections.append(conn)
|
|
@@ -142,13 +140,12 @@ class AdapterSession(threading.Thread):
|
|
|
142
140
|
except Exception as e:
|
|
143
141
|
error_message = make_error_description(e)
|
|
144
142
|
|
|
145
|
-
|
|
146
143
|
self._logger.critical(
|
|
147
144
|
f"Error in {self._adapter.descriptor} session loop : {error_message}"
|
|
148
145
|
)
|
|
149
146
|
try:
|
|
150
147
|
error_message = make_error_description(e)
|
|
151
|
-
|
|
148
|
+
|
|
152
149
|
for conn in self.connections:
|
|
153
150
|
frontend_send(conn.conn, Action.ERROR_GENERIC, error_message)
|
|
154
151
|
except Exception:
|
|
@@ -167,15 +164,13 @@ class AdapterSession(threading.Thread):
|
|
|
167
164
|
# The wait has a timeout set by the adapter, it corresponds to the current continuation/total timeout
|
|
168
165
|
|
|
169
166
|
# Create a list of what is awaited
|
|
170
|
-
wait_list: list[Selectable] = [
|
|
171
|
-
conn.conn for conn in self.connections
|
|
172
|
-
]
|
|
167
|
+
wait_list: list[Selectable] = [conn.conn for conn in self.connections]
|
|
173
168
|
adapter_fd = self._adapter.selectable()
|
|
174
169
|
if adapter_fd is not None and adapter_fd.fileno() >= 0:
|
|
175
170
|
wait_list.append(adapter_fd)
|
|
176
171
|
|
|
177
172
|
wait_list.append(self._new_connection_r)
|
|
178
|
-
|
|
173
|
+
|
|
179
174
|
timeout_timestamp = None
|
|
180
175
|
event = None
|
|
181
176
|
|
|
@@ -183,8 +178,11 @@ class AdapterSession(threading.Thread):
|
|
|
183
178
|
if adapter_timestamp is not None:
|
|
184
179
|
timeout_timestamp = nmin(timeout_timestamp, adapter_timestamp)
|
|
185
180
|
event = TimeoutEvent.ADAPTER
|
|
186
|
-
|
|
187
|
-
if
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
timeout_timestamp is None
|
|
184
|
+
or self._next_monitoring_timestamp < timeout_timestamp
|
|
185
|
+
):
|
|
188
186
|
timeout_timestamp = self._next_monitoring_timestamp
|
|
189
187
|
event = TimeoutEvent.MONITORING
|
|
190
188
|
|
|
@@ -253,7 +251,6 @@ class AdapterSession(threading.Thread):
|
|
|
253
251
|
return
|
|
254
252
|
try:
|
|
255
253
|
request = conn.conn.recv()
|
|
256
|
-
request_timestamp = time.time()
|
|
257
254
|
except (EOFError, ConnectionResetError) as e:
|
|
258
255
|
# Probably a ping or an error
|
|
259
256
|
self._logger.warning(
|
|
@@ -280,7 +277,7 @@ class AdapterSession(threading.Thread):
|
|
|
280
277
|
response_action = Action.OPEN
|
|
281
278
|
else:
|
|
282
279
|
response_action = Action.ERROR_FAILED_TO_OPEN
|
|
283
|
-
extra_arguments = ("",)
|
|
280
|
+
extra_arguments = ("",)
|
|
284
281
|
case Action.WRITE:
|
|
285
282
|
data = request[1]
|
|
286
283
|
if self._adapter.is_opened():
|
|
@@ -302,9 +299,9 @@ class AdapterSession(threading.Thread):
|
|
|
302
299
|
case Action.PING:
|
|
303
300
|
response_action, extra_arguments = Action.PING, ()
|
|
304
301
|
case Action.SET_STOP_CONDITIONs:
|
|
305
|
-
self._adapter.set_stop_conditions(
|
|
306
|
-
stop_condition_to_backend(sc) for sc in request[1]
|
|
307
|
-
|
|
302
|
+
self._adapter.set_stop_conditions(
|
|
303
|
+
[stop_condition_to_backend(sc) for sc in request[1]]
|
|
304
|
+
)
|
|
308
305
|
response_action, extra_arguments = (
|
|
309
306
|
Action.SET_STOP_CONDITIONs,
|
|
310
307
|
(),
|
|
@@ -315,7 +312,9 @@ class AdapterSession(threading.Thread):
|
|
|
315
312
|
case Action.START_READ:
|
|
316
313
|
response_time = float(request[1])
|
|
317
314
|
self._adapter.start_read(response_time, self._read_init_id)
|
|
318
|
-
response_action, extra_arguments = Action.START_READ, (
|
|
315
|
+
response_action, extra_arguments = Action.START_READ, (
|
|
316
|
+
self._read_init_id,
|
|
317
|
+
)
|
|
319
318
|
self._read_init_id += 1
|
|
320
319
|
|
|
321
320
|
# case Action.GET_BACKEND_TIME:
|
|
File without changes
|