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.
@@ -7,27 +7,27 @@
7
7
 
8
8
  import logging
9
9
  import socket
10
- import uuid
11
10
  from abc import ABC, abstractmethod
12
11
  from collections.abc import Generator
13
12
  from dataclasses import dataclass
14
13
  from enum import Enum
15
14
  from multiprocessing.connection import Connection
16
15
  from threading import Thread
17
- from time import time
18
- from typing import Protocol
16
+ import time
17
+ from typing import Protocol, cast
18
+
19
+ from syndesi.tools.types import NumberLike
19
20
 
20
21
  from ...tools.backend_api import AdapterBackendStatus, Fragment
21
22
  from ...tools.log_settings import LoggerAlias
22
23
  from .descriptors import Descriptor
23
24
  from .stop_condition_backend import (
24
- #LengthBackend,
25
+ ContinuationBackend,
25
26
  StopConditionBackend,
26
- #TerminationBackend,
27
- #TimeoutStopConditionBackend,
27
+ TotalBackend,
28
28
  )
29
29
 
30
- from ..stop_condition import StopConditionType, TimeoutStopCondition
30
+ from ..stop_condition import Continuation, StopConditionType
31
31
 
32
32
 
33
33
  class HasFileno(Protocol):
@@ -72,13 +72,22 @@ class AdapterDisconnected(AdapterSignal):
72
72
  return self.__str__()
73
73
 
74
74
 
75
+ # @dataclass
76
+ # class AdapterReadInit(AdapterSignal):
77
+ # end_delay : float | None
78
+
79
+ # def __str__(self) -> str:
80
+ # return f"Read init (end delay : {self.end_delay})"
81
+
82
+ # def __repr__(self) -> str:
83
+ # return self.__str__()
84
+
75
85
  @dataclass
76
- class AdapterReadInit(AdapterSignal):
77
- received_response_in_time: bool
78
- uuid: uuid.UUID
86
+ class AdapterResponseTimeout(AdapterSignal):
87
+ identifier : int
79
88
 
80
89
  def __str__(self) -> str:
81
- return f"Read init [{str(self.uuid)[:5]}...] {'in time' if self.received_response_in_time else 'not in time'}"
90
+ return f"Response timeout"
82
91
 
83
92
  def __repr__(self) -> str:
84
93
  return self.__str__()
@@ -90,9 +99,9 @@ class AdapterReadPayload(AdapterSignal):
90
99
  stop_timestamp: float
91
100
  stop_condition_type: StopConditionType
92
101
  previous_read_buffer_used: bool
93
- response_timestamp : float
102
+ response_timestamp : float | None
94
103
  # Only used by client and set by frontend
95
- response_delay : float = 0.0
104
+ response_delay : float | None = None
96
105
 
97
106
  def data(self) -> bytes:
98
107
  return b"".join([f.data for f in self.fragments])
@@ -109,8 +118,17 @@ class AdapterReadPayload(AdapterSignal):
109
118
  @dataclass
110
119
  class ResponseRequest:
111
120
  timestamp: float
112
- uuid: uuid.UUID
121
+ identifier : int
113
122
 
123
+ def nmin(a : float | None, b : float | None) -> float | None:
124
+ if a is None and b is None:
125
+ return None
126
+ elif a is None:
127
+ return b
128
+ elif b is None:
129
+ return a
130
+ else:
131
+ return min(a, b)
114
132
 
115
133
  class AdapterBackend(ABC):
116
134
  class ThreadCommands(Enum):
@@ -118,7 +136,7 @@ class AdapterBackend(ABC):
118
136
 
119
137
  class AdapterTimeoutEventOrigin(Enum):
120
138
  TIMEOUT = 0
121
- RESPONSE_READ_INIT = 1
139
+ RESPONSE_REQUEST = 1
122
140
 
123
141
  def __init__(self, descriptor: Descriptor) -> None:
124
142
  """
@@ -136,22 +154,21 @@ class AdapterBackend(ABC):
136
154
  super().__init__()
137
155
 
138
156
  # TODO : Switch to multiple stop conditios
139
- self._stop_conditions: list[StopConditionBackend] = [StopConditionBackend(TimeoutStopCondition(
140
- continuation=0.1, total=None
141
- ))]
157
+ self._stop_conditions: list[StopConditionBackend] = [
158
+ ContinuationBackend(time=0.1)
159
+ ]
142
160
  self.descriptor = descriptor
143
161
  self._thread: Thread | None = None
144
162
  self._status = AdapterBackendStatus.DISCONNECTED
145
163
  self._thread_commands_read, self._thread_commands_write = socket.socketpair()
146
164
  self.backend_signal: Connection | None = None
147
165
  self.fragments: list[Fragment] = []
148
- self._start_read_timestamp: float | None = None
149
- # self._data_out_queue = []
150
166
  self._next_timeout_timestamp: float | None = None
151
167
  # _response_time indicates if the frontend asked for a read
152
168
  # None : No ask
153
169
  # float : Ask for a response to happen at the specified value at max
154
170
  self._response_request: ResponseRequest | None = None
171
+ self._response_request_start : float | None = None
155
172
 
156
173
  self._first_fragment = True
157
174
 
@@ -159,7 +176,7 @@ class AdapterBackend(ABC):
159
176
  # not used because of termination or length stop condition
160
177
  self._previous_buffer = Fragment(b"", None)
161
178
 
162
- self._last_write_time = time()
179
+ self._last_write_time = time.time()
163
180
 
164
181
  def set_stop_conditions(self, stop_conditions: list[StopConditionBackend]) -> None:
165
182
  """
@@ -204,14 +221,6 @@ class AdapterBackend(ABC):
204
221
  """
205
222
  Stop communication with the device
206
223
  """
207
- self._logger.debug("Closing adapter and stopping read thread")
208
- # self._thread_commands_write.send(self.ThreadCommands.STOP.value)
209
- # if self._thread is not None and self._thread.is_alive():
210
- # try:
211
- # self._thread.join()
212
- # except RuntimeError:
213
- # # If the thread cannot be joined, then so be it
214
- # pass
215
224
  self._status = AdapterBackendStatus.DISCONNECTED
216
225
  return True
217
226
 
@@ -223,7 +232,7 @@ class AdapterBackend(ABC):
223
232
  ----------
224
233
  data : bytes or str
225
234
  """
226
- self._last_write_time = time()
235
+ self._last_write_time = time.time()
227
236
  self._logger.debug(f"Write {repr(data)}")
228
237
  return True
229
238
 
@@ -238,9 +247,9 @@ class AdapterBackend(ABC):
238
247
  def _socket_read(self) -> Fragment:
239
248
  raise NotImplementedError
240
249
 
241
- def _fragments_to_string(self, fragments : list[bytes]):
250
+ def _fragments_to_string(self, fragments : list[Fragment]) -> str:
242
251
  if len(fragments) > 0:
243
- return '+'.join(repr(f) for f in fragments)
252
+ return '+'.join(repr(f.data) for f in fragments)
244
253
  else:
245
254
  return str([])
246
255
 
@@ -256,28 +265,26 @@ class AdapterBackend(ABC):
256
265
  else:
257
266
  self._logger.debug(f"New fragment {fragment_delta_t:+.3f} {fragment}" + (" (first)" if self._first_fragment else ""))
258
267
  if self._status == AdapterBackendStatus.CONNECTED:
259
- t = time()
268
+ t = time.time()
269
+
270
+ # If there's a response request, disable it if there's a timeout stop condition
271
+ # The stop-condition will do the job
272
+
273
+ if self._response_request is not None:
274
+ for stop_condition in self._stop_conditions:
275
+ if isinstance(stop_condition, (ContinuationBackend, TotalBackend)):
276
+ self._response_request = None
277
+ break
260
278
 
261
279
  while True:
262
280
  if self._first_fragment:
263
- self._read_start_time = t
281
+ self._first_fragment = False
282
+ self._read_start_timestamp = t
264
283
  for stop_condition in self._stop_conditions:
265
284
  stop_condition.initiate_read()
266
- self._first_fragment = False
267
- if self._response_request is not None:
268
- received_response_in_time = (
269
- t < self._response_request.timestamp
270
- )
271
- # The frontend asked for a response, tell it
272
- yield AdapterReadInit(
273
- received_response_in_time, self._response_request.uuid
274
- )
275
- self._response_request = None
276
-
277
285
 
278
286
  stop = False
279
287
  kept = fragment
280
- self._next_timeout_timestamp
281
288
 
282
289
  # Run each stop condition one after the other, if a stop is reached, stop evaluating
283
290
  stop_condition_type : StopConditionType
@@ -285,23 +292,35 @@ class AdapterBackend(ABC):
285
292
  stop, kept, self._previous_buffer, self._next_timeout_timestamp = \
286
293
  stop_condition.evaluate(kept)
287
294
  if stop:
288
- stop_condition_type = stop_condition.stop_condition.type()
295
+ stop_condition_type = stop_condition.type()
289
296
  break
290
297
 
291
298
 
292
- if kept.data != b'':
293
- self.fragments.append(kept)
299
+ # if kept.data != b'':
300
+ # self.fragments.append(kept)
301
+
302
+ self.fragments.append(kept)
294
303
 
295
304
  if stop:
296
305
  self._first_fragment = True
297
306
  self._logger.debug(f"Payload {self._fragments_to_string(self.fragments)} ({stop_condition_type.value})")
307
+ if self._response_request_start is None or len(self.fragments) == 0:
308
+ response_delay = None
309
+ else:
310
+ if self.fragments[0].timestamp is None:
311
+ response_delay = None
312
+ else:
313
+ response_delay = self.fragments[0].timestamp - self._response_request_start
314
+ self._response_request_start = None
298
315
  yield AdapterReadPayload(
299
316
  fragments=self.fragments,
300
317
  stop_timestamp=t,
301
318
  stop_condition_type=stop_condition_type,
302
319
  previous_read_buffer_used=False,
303
- response_timestamp=self.fragments[0].timestamp
320
+ response_timestamp=self.fragments[0].timestamp,
321
+ response_delay=response_delay
304
322
  )
323
+ self._next_timeout_timestamp = None # Experiment !
305
324
  self.fragments.clear()
306
325
 
307
326
  if len(self._previous_buffer.data) > 0 and stop:
@@ -315,15 +334,15 @@ class AdapterBackend(ABC):
315
334
 
316
335
  return None
317
336
 
318
- def start_read(self, response_time: float, uuid: uuid.UUID) -> None:
337
+ def start_read(self, response_time: float, identifier: int) -> None:
319
338
  """
320
339
  Start a read operation. This is a signal from the frontend. The only goal is to set the response time
321
340
  and tell the frontend if nothing arrives within a set time
322
341
  """
323
- t = time()
324
- self._start_read_timestamp = t
325
- self._logger.debug(f"Setup read [{str(uuid)[:5]}...] in {response_time:.3f} s")
326
- self._response_request = ResponseRequest(t + response_time, uuid)
342
+ self._response_request_start = time.time()
343
+ self._logger.debug(f"Setup read [{identifier}] in {response_time:.3f} s")
344
+ self._response_request = ResponseRequest(self._response_request_start + response_time, identifier)
345
+
327
346
 
328
347
  @abstractmethod
329
348
  def is_opened(self) -> bool:
@@ -338,31 +357,44 @@ class AdapterBackend(ABC):
338
357
  return self.__str__()
339
358
 
340
359
  def on_timeout_event(self) -> AdapterSignal | None:
341
- t = time()
360
+ t = time.time()
342
361
 
343
362
  if self._next_timeout_origin == self.AdapterTimeoutEventOrigin.TIMEOUT:
344
363
  self._next_timeout_timestamp = None
345
364
  self._first_fragment = True
346
- self._logger.debug(f"Payload {self._fragments_to_string(self.fragments)} ({StopConditionType.TIMEOUT.value})")
365
+ if self._response_request_start is None or len(self.fragments) == 0:
366
+ response_delay = None
367
+ else:
368
+ if self.fragments[0].timestamp is not None:
369
+ response_delay = self.fragments[0].timestamp - self._response_request_start
370
+ else:
371
+ response_delay = None
372
+ self._response_request_start = None
373
+
347
374
  output = AdapterReadPayload(
348
375
  stop_timestamp=t,
349
376
  stop_condition_type=StopConditionType.TIMEOUT,
350
377
  previous_read_buffer_used=False,
351
378
  fragments=self.fragments,
352
- response_timestamp=self.fragments[0].timestamp
379
+ response_timestamp=self.fragments[0].timestamp if len(self.fragments) > 0 else None,
380
+ response_delay=response_delay
353
381
  )
382
+ # Reset response request
383
+ if self._response_request is not None:
384
+ self._response_request = None
385
+ self._response_request_start = None
354
386
  # Clear all of the fragments
355
387
  self.fragments = []
356
388
  return output
357
389
 
358
390
  elif (
359
391
  self._next_timeout_origin
360
- == self.AdapterTimeoutEventOrigin.RESPONSE_READ_INIT
392
+ == self.AdapterTimeoutEventOrigin.RESPONSE_REQUEST
361
393
  ):
362
394
  if self._response_request is not None:
363
- uuid = self._response_request.uuid
395
+ signal = AdapterResponseTimeout(self._response_request.identifier)
364
396
  self._response_request = None
365
- return AdapterReadInit(False, uuid)
397
+ return signal
366
398
 
367
399
  return None
368
400
 
@@ -370,6 +402,8 @@ class AdapterBackend(ABC):
370
402
  min_timestamp = None
371
403
  self._next_timeout_origin = None
372
404
 
405
+ t = time.time()
406
+
373
407
  if self._next_timeout_timestamp is not None:
374
408
  min_timestamp = self._next_timeout_timestamp
375
409
  self._next_timeout_origin = self.AdapterTimeoutEventOrigin.TIMEOUT
@@ -381,7 +415,6 @@ class AdapterBackend(ABC):
381
415
  ):
382
416
  min_timestamp = self._response_request.timestamp
383
417
  self._next_timeout_origin = (
384
- self.AdapterTimeoutEventOrigin.RESPONSE_READ_INIT
418
+ self.AdapterTimeoutEventOrigin.RESPONSE_REQUEST
385
419
  )
386
-
387
420
  return min_timestamp
@@ -13,7 +13,8 @@ from enum import Enum
13
13
  from multiprocessing.connection import Pipe, wait
14
14
  from typing import Any, Tuple
15
15
 
16
- from syndesi.adapters.backend.stop_condition_backend import StopConditionBackend
16
+ from syndesi.adapters.backend.stop_condition_backend import StopConditionBackend, stop_condition_to_backend
17
+ from syndesi.tools.errors import make_error_description
17
18
  from syndesi.tools.types import NumberLike
18
19
 
19
20
  from ...tools.backend_api import Action, frontend_send
@@ -21,9 +22,10 @@ from ...tools.log_settings import LoggerAlias
21
22
  from .adapter_backend import (
22
23
  AdapterBackend,
23
24
  AdapterDisconnected,
24
- AdapterReadInit,
25
+ #AdapterReadInit,
25
26
  AdapterReadPayload,
26
27
  Selectable,
28
+ nmin,
27
29
  )
28
30
  from .backend_tools import NamedConnection
29
31
  from .descriptors import (
@@ -37,52 +39,13 @@ from .ip_backend import IPBackend
37
39
  from .serialport_backend import SerialPortBackend
38
40
  #from .stop_condition_backend import stop_condition_from_list
39
41
  from .visa_backend import VisaBackend
40
-
42
+ from pathlib import Path
41
43
 
42
44
 
43
45
  class TimeoutEvent(Enum):
44
46
  MONITORING = 0
45
47
  ADAPTER = 1
46
- CONNECTIONS = 2
47
-
48
- class TimeoutManager:
49
- def __init__(self, monitoring_delay : float) -> None:
50
- self._monitoring_delay = monitoring_delay
51
- self._timeouts : list[Tuple[TimeoutEvent, float]] = []
52
- self._monitoring_timestamp = time.time()
53
-
54
- def get_next_timeout(self) -> Tuple[TimeoutEvent, float]:
55
- t = time.time()
56
- # Sort the list
57
- self._timeouts.sort(key = lambda x : x[1], reverse=True)
58
- # Check if the first element of the list is first or if it's the monitoring timeout
59
- if len(self._timeouts) > 0 and self._timeouts[0][1] < self._monitoring_timestamp:
60
- # A timeout is first
61
- event, timestamp = self._timeouts.pop(0)
62
- delta = max(0, timestamp - t)
63
- return event, delta
64
- else:
65
- # Monitoring is first
66
- self._set_next_monitoring_delay()
67
- delta = max(0, self._monitoring_timestamp - t)
68
- return TimeoutEvent.MONITORING, delta
69
-
70
- def _set_next_monitoring_delay(self):
71
- t = time.time()
72
- self._monitoring_timestamp = t + self._monitoring_delay
73
-
74
- def add_timeout_absolute(self, event : TimeoutEvent, timestamp : float):
75
- self._timeouts.append((event, timestamp))
76
-
77
- def add_timeout_relative(self, event : TimeoutEvent, delay : float):
78
- self.add_timeout_absolute(event, time.time() + delay)
79
-
80
- def has_event(self, event : TimeoutEvent) -> bool:
81
- for _event, _ in self._timeouts:
82
- if _event == event:
83
- return True
84
- return False
85
-
48
+ #CONNECTIONS = 2
86
49
 
87
50
  def get_adapter(descriptor: Descriptor) -> AdapterBackend:
88
51
  # The adapter doesn't exist, create it
@@ -109,6 +72,7 @@ class AdapterSession(threading.Thread):
109
72
  self._logger = logging.getLogger(LoggerAlias.ADAPTER_BACKEND.value)
110
73
  self._logger.setLevel("DEBUG")
111
74
  self._role = None
75
+ self._next_monitoring_timestamp = time.time() + self.MONITORING_DELAY
112
76
 
113
77
  # self._stop_flag = False
114
78
  self._connections_lock = threading.Lock()
@@ -137,7 +101,8 @@ class AdapterSession(threading.Thread):
137
101
 
138
102
  #self._timeout_events: list[tuple[TimeoutEvent, float]] = []
139
103
 
140
- self._timeout_manager = TimeoutManager(self.MONITORING_DELAY)
104
+ self._read_init_id = 0
105
+
141
106
 
142
107
 
143
108
  def add_connection(self, conn: NamedConnection) -> None:
@@ -151,40 +116,8 @@ class AdapterSession(threading.Thread):
151
116
  with self._connections_lock:
152
117
  if conn in self.connections:
153
118
  conn.conn.close()
154
- # self.connection_names.pop(id(conn), None)
155
119
  self.connections.remove(conn)
156
120
 
157
- # def _pop_next_timeout_event(self) -> tuple[TimeoutEvent | None, float | None]:
158
- # if len(self._timeout_events) > 0:
159
- # self._timeout_events.sort(key=lambda x: x[1], reverse=True)
160
- # return self._timeout_events.pop(0)
161
- # else:
162
- # return None, None
163
-
164
- # def _has_timeout_event(self, event: TimeoutEvent) -> bool:
165
- # for event, _ in self._timeout_events:
166
- # if event == event:
167
- # return True
168
- # return False
169
-
170
- # def _add_timeout_event(self, event: TimeoutEvent, timestamp: float) -> None:
171
- # self._timeout_events.append((event, timestamp))
172
-
173
- # key_timeouts = [
174
- # (TimeoutEvent.MONITORING, self._timeout_events[TimeoutEvent.MONITORING]),
175
- # (TimeoutEvent.ADAPTER, self._timeout_events[TimeoutEvent.ADAPTER]),
176
- # ] + list(self._timeout_events[TimeoutEvent.CONNECTIONS].items())
177
-
178
- # key, timeout = None, None
179
- # for k, t in key_timeouts:
180
- # if t is not Ellipsis:
181
- # if isinstance(t, float) or isinstance(t, int):
182
- # if timeout is None or t < timeout:
183
- # timeout = t
184
- # key = k
185
-
186
- # return key, timeout
187
-
188
121
  def send(self, conn: NamedConnection, action: Action, *args: Any) -> None:
189
122
  if not frontend_send(conn.conn, action, *args):
190
123
  self._logger.warning(f"Failed to send to {conn.remote()}")
@@ -201,35 +134,23 @@ class AdapterSession(threading.Thread):
201
134
  return self._adapter.is_opened()
202
135
 
203
136
  def run(self) -> None:
204
-
205
137
  while True:
206
138
  try:
207
139
  stop = self.loop()
208
140
  if stop:
209
141
  break
210
142
  except Exception as e:
211
- tb = e.__traceback__
212
- if tb is None:
213
- error_message = ""
214
- else:
215
- while tb.tb_next is not None:
216
- tb = tb.tb_next
217
- _type = type(e)
218
- extra_arguments = (str(e),)
219
- line_no = tb.tb_lineno
220
- frame = tb.tb_frame
221
- filename = frame.f_code.co_filename
222
- error_message = (
223
- f"{_type} : {extra_arguments} {filename}:{line_no}"
224
- )
143
+ error_message = make_error_description(e)
225
144
 
226
145
 
227
146
  self._logger.critical(
228
147
  f"Error in {self._adapter.descriptor} session loop : {error_message}"
229
148
  )
230
149
  try:
150
+ error_message = make_error_description(e)
151
+
231
152
  for conn in self.connections:
232
- frontend_send(conn.conn, Action.ERROR_GENERIC, str(e))
153
+ frontend_send(conn.conn, Action.ERROR_GENERIC, error_message)
233
154
  except Exception:
234
155
  break
235
156
  self._logger.info(f"Exit {self._adapter.descriptor} session loop")
@@ -253,30 +174,30 @@ class AdapterSession(threading.Thread):
253
174
  if adapter_fd is not None and adapter_fd.fileno() >= 0:
254
175
  wait_list.append(adapter_fd)
255
176
 
256
- next_adapter_timeout = self._adapter.get_next_timeout()
257
- if (
258
- not self._timeout_manager.has_event(TimeoutEvent.ADAPTER)
259
- and next_adapter_timeout is not None
260
- ):
261
- self._timeout_manager.add_timeout_absolute(TimeoutEvent.ADAPTER, next_adapter_timeout)
262
-
263
177
  wait_list.append(self._new_connection_r)
178
+
179
+ timeout_timestamp = None
180
+ event = None
181
+
182
+ adapter_timestamp = self._adapter.get_next_timeout()
183
+ if adapter_timestamp is not None:
184
+ timeout_timestamp = nmin(timeout_timestamp, adapter_timestamp)
185
+ event = TimeoutEvent.ADAPTER
264
186
 
265
- # event, timeout_timestamp = self._pop_next_timeout_event()
266
- # if timeout_timestamp is None:
267
- # timeout = None
268
- # else:
269
- # timeout = timeout_timestamp - time.time()
270
-
187
+ if timeout_timestamp is None or self._next_monitoring_timestamp < timeout_timestamp:
188
+ timeout_timestamp = self._next_monitoring_timestamp
189
+ event = TimeoutEvent.MONITORING
271
190
 
272
- #asd
273
- # Probably an infinite wait here that blocks the monitoring
274
- event, timeout = self._timeout_manager.get_next_timeout()
191
+ if timeout_timestamp is None:
192
+ timeout = None
193
+ else:
194
+ timeout = timeout_timestamp - time.time()
275
195
  ready = wait(wait_list, timeout=timeout) # type: ignore
276
-
196
+ t = time.time()
277
197
  if len(ready) == 0:
278
198
  # Timeout event
279
199
  if event == TimeoutEvent.MONITORING:
200
+ self._next_monitoring_timestamp = t + self.MONITORING_DELAY
280
201
  stop = self._monitor()
281
202
  if stop:
282
203
  return True
@@ -285,26 +206,17 @@ class AdapterSession(threading.Thread):
285
206
  if signal is not None:
286
207
  # The signal can be none if it has been disabled in the meantime
287
208
  self._logger.debug(f"Adapter signal (timeout) : {signal}")
288
- if isinstance(signal, AdapterReadPayload):
289
- self.send_to_all(Action.ADAPTER_EVENT_DATA_READY, signal)
290
- elif isinstance(signal, AdapterReadInit):
291
- self.send_to_all(Action.ADAPTER_EVENT_READ_INIT, signal)
209
+ self.send_to_all(Action.ADAPTER_SIGNAL, signal)
210
+
292
211
  # Main adapter loop
293
212
  if self._new_connection_r in ready:
294
213
  # New connection event
295
- # os.read(self._new_connection_r, 1)
296
214
  self._new_connection_r.recv()
297
215
  # Adapter event
298
216
  if self._adapter.selectable() in ready:
299
217
  for signal in self._adapter.on_socket_ready():
300
218
  self._logger.debug(f"Adapter signal (selectable) : {signal}")
301
- if isinstance(signal, AdapterDisconnected):
302
- # TODO : Maybe use Action.EVENT only and the signal specifies which one it is
303
- self.send_to_all(Action.ADAPTER_EVENT_DISCONNECTED, signal)
304
- elif isinstance(signal, AdapterReadInit):
305
- self.send_to_all(Action.ADAPTER_EVENT_READ_INIT, signal)
306
- elif isinstance(signal, AdapterReadPayload):
307
- self.send_to_all(Action.ADAPTER_EVENT_DATA_READY, signal)
219
+ self.send_to_all(Action.ADAPTER_SIGNAL, signal)
308
220
 
309
221
  for conn in self.connections:
310
222
  if conn.conn in ready:
@@ -361,18 +273,14 @@ class AdapterSession(threading.Thread):
361
273
  match action:
362
274
  case Action.OPEN:
363
275
  self._adapter.set_stop_conditions(
364
- [StopConditionBackend(sc) for sc in request[1]]
276
+ [stop_condition_to_backend(sc) for sc in request[1]]
365
277
  )
366
278
  if self._adapter.open():
367
279
  # Success !
368
280
  response_action = Action.OPEN
369
281
  else:
370
282
  response_action = Action.ERROR_FAILED_TO_OPEN
371
- extra_arguments = ("",)
372
- case Action.FORCE_CLOSE:
373
- self._adapter.close()
374
- remove_after_response = True
375
- response_action, extra_arguments = Action.FORCE_CLOSE, ()
283
+ extra_arguments = ("",)
376
284
  case Action.WRITE:
377
285
  data = request[1]
378
286
  if self._adapter.is_opened():
@@ -393,48 +301,40 @@ class AdapterSession(threading.Thread):
393
301
  self._logger.error("Could not write, adapter is closed")
394
302
  case Action.PING:
395
303
  response_action, extra_arguments = Action.PING, ()
396
- case Action.SET_STOP_CONDITION:
397
- stop_conditions = stop_conditions_to_backends(request[1])
398
- self._adapter.set_stop_conditions(stop_conditions)
304
+ case Action.SET_STOP_CONDITIONs:
305
+ self._adapter.set_stop_conditions([
306
+ stop_condition_to_backend(sc) for sc in request[1]
307
+ ])
399
308
  response_action, extra_arguments = (
400
- Action.SET_STOP_CONDITION,
309
+ Action.SET_STOP_CONDITIONs,
401
310
  (),
402
311
  )
403
312
  case Action.FLUSHREAD:
404
313
  self._adapter.flush_read()
405
314
  response_action, extra_arguments = Action.FLUSHREAD, ()
406
- # case Action.START_READ:
407
- # response_time = float(request[1])
408
- # uuid = request[2]
409
- # self._adapter.start_read(response_time, uuid)
410
- # response_action, extra_arguments = Action.START_READ, ()
411
- case Action.GET_BACKEND_TIME:
412
- response_action = Action.GET_BACKEND_TIME
413
- extra_arguments = (request_timestamp, )
315
+ case Action.START_READ:
316
+ response_time = float(request[1])
317
+ self._adapter.start_read(response_time, self._read_init_id)
318
+ response_action, extra_arguments = Action.START_READ, (self._read_init_id,)
319
+ self._read_init_id += 1
320
+
321
+ # case Action.GET_BACKEND_TIME:
322
+ # response_action = Action.GET_BACKEND_TIME
323
+ # extra_arguments = (request_timestamp, )
414
324
  case Action.CLOSE:
325
+ force = request[1]
415
326
  # Close this connection
416
327
  remove_after_response = True
417
328
  response_action, extra_arguments = Action.CLOSE, ()
329
+ if force:
330
+ self._adapter.close()
418
331
  case _:
419
332
  response_action, extra_arguments = (
420
333
  Action.ERROR_UNKNOWN_ACTION,
421
334
  (f"{action}",),
422
335
  )
423
336
  except Exception as e:
424
- tb = e.__traceback__
425
- if tb is None:
426
- error_message = ""
427
- else:
428
- while tb.tb_next is not None:
429
- tb = tb.tb_next
430
- _type = type(e)
431
- extra_arguments = (str(e),)
432
- line_no = tb.tb_lineno
433
- frame = tb.tb_frame
434
- filename = frame.f_code.co_filename
435
- error_message = (
436
- f"{_type} : {extra_arguments} {filename}:{line_no}"
437
- )
337
+ error_message = make_error_description(e)
438
338
 
439
339
  response_action, extra_arguments = (
440
340
  Action.ERROR_GENERIC,