syndesi 0.4.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. {syndesi-0.4.0/syndesi.egg-info → syndesi-0.4.1}/PKG-INFO +1 -1
  2. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/adapter.py +52 -123
  3. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/auto.py +1 -1
  4. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/adapter_backend.py +0 -1
  5. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/adapter_session.py +2 -2
  6. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/descriptors.py +3 -2
  7. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/ip_backend.py +1 -0
  8. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/serialport_backend.py +5 -5
  9. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/visa_backend.py +7 -7
  10. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/ip.py +6 -10
  11. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/timeout.py +1 -1
  12. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/visa.py +2 -2
  13. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/delimited.py +16 -21
  14. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/modbus.py +17 -14
  15. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/raw.py +20 -16
  16. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/scpi.py +17 -15
  17. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/backend_api.py +1 -1
  18. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/version.py +1 -1
  19. {syndesi-0.4.0 → syndesi-0.4.1/syndesi.egg-info}/PKG-INFO +1 -1
  20. {syndesi-0.4.0 → syndesi-0.4.1}/LICENSE +0 -0
  21. {syndesi-0.4.0 → syndesi-0.4.1}/README.md +0 -0
  22. {syndesi-0.4.0 → syndesi-0.4.1}/pyproject.toml +0 -0
  23. {syndesi-0.4.0 → syndesi-0.4.1}/setup.cfg +0 -0
  24. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/__init__.py +0 -0
  25. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/__main__.py +0 -0
  26. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/__init__.py +0 -0
  27. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/__init__.py +0 -0
  28. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/adapter_manager.py +0 -0
  29. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/backend.py +0 -0
  30. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/backend_tools.py +0 -0
  31. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/stop_condition_backend.py +0 -0
  32. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/timed_queue.py +0 -0
  33. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/timeout.py +0 -0
  34. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/ip_server.py +0 -0
  35. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/serialport.py +0 -0
  36. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/stop_condition.py +0 -0
  37. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/__init__.py +0 -0
  38. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/backend_console.py +0 -0
  39. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/backend_status.py +0 -0
  40. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/backend_wrapper.py +0 -0
  41. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/console.py +0 -0
  42. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/shell.py +0 -0
  43. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/shell_tools.py +0 -0
  44. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/terminal.py +0 -0
  45. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/terminal_apps.py +0 -0
  46. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/terminal_tools.py +0 -0
  47. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/__init__.py +0 -0
  48. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/protocol.py +0 -0
  49. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/scripts/__init__.py +0 -0
  50. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/scripts/syndesi.py +0 -0
  51. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/scripts/syndesi_backend.py +0 -0
  52. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/__init__.py +0 -0
  53. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/backend_logger.py +0 -0
  54. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/errors.py +0 -0
  55. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/exceptions.py +0 -0
  56. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/internal.py +0 -0
  57. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/log.py +0 -0
  58. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/log_settings.py +0 -0
  59. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/types.py +0 -0
  60. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi.egg-info/SOURCES.txt +0 -0
  61. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi.egg-info/dependency_links.txt +0 -0
  62. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi.egg-info/entry_points.txt +0 -0
  63. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi.egg-info/requires.txt +0 -0
  64. {syndesi-0.4.0 → syndesi-0.4.1}/syndesi.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syndesi
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Syndesi
5
5
  Author-email: Sébastien Deriaz <sebastien.deriaz1@gmail.com>
6
6
  License: GPL
@@ -37,6 +37,7 @@ from ..tools.backend_api import (
37
37
  BACKEND_PORT,
38
38
  Action,
39
39
  BackendResponse,
40
+ Fragment,
40
41
  default_host,
41
42
  raise_if_error,
42
43
  EXTRA_BUFFER_RESPONSE_TIME
@@ -49,7 +50,7 @@ from .backend.adapter_backend import (
49
50
  AdapterSignal,
50
51
  )
51
52
  from .backend.descriptors import Descriptor
52
- from .stop_condition import StopCondition, Continuation, Total
53
+ from .stop_condition import StopCondition, Continuation, StopConditionType, Total
53
54
  from .timeout import Timeout, TimeoutAction, any_to_timeout
54
55
 
55
56
  DEFAULT_STOP_CONDITION = Continuation(time=0.1)
@@ -385,7 +386,7 @@ class Adapter(ABC):
385
386
  elif stop_conditions is None:
386
387
  self._stop_conditions = []
387
388
 
388
- self._make_backend_request(Action.SET_STOP_CONDITION, self._stop_conditions)
389
+ self._make_backend_request(Action.SET_STOP_CONDITIONs, self._stop_conditions)
389
390
 
390
391
  def set_default_stop_condition(self, stop_condition: StopCondition) -> None:
391
392
  """
@@ -462,9 +463,9 @@ class Adapter(ABC):
462
463
  def read_detailed(
463
464
  self,
464
465
  timeout: Timeout | EllipsisType | None = ...,
465
- stop_condition: StopCondition | EllipsisType | None = ...,
466
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
466
467
  scope : str = ReadScope.BUFFERED.value,
467
- ) -> AdapterReadPayload | None:
468
+ ) -> AdapterReadPayload:
468
469
  """
469
470
  Read data from the device
470
471
 
@@ -481,9 +482,22 @@ class Adapter(ABC):
481
482
  data : bytes
482
483
  signal : AdapterReadPayload
483
484
  """
484
- t = time.time()
485
485
  _scope = ReadScope(scope)
486
486
  output_signal = None
487
+ read_timeout = None
488
+
489
+ if timeout is ...:
490
+ read_timeout = self._timeout
491
+ else:
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)
487
501
 
488
502
  # First, we check if data is in the buffer and if the scope if set to BUFFERED
489
503
  while _scope == ReadScope.BUFFERED and self._signal_queue.has_read_payload():
@@ -495,30 +509,25 @@ class Adapter(ABC):
495
509
  else:
496
510
  # Nothing was found, ask the backend with a START_READ request. The backend will
497
511
  # respond at most after the response_time with either data or a RESPONSE_TIMEOUT
498
- if timeout is ...:
499
- read_timeout = self._timeout
500
- else:
501
- read_timeout = any_to_timeout(timeout)
502
512
 
503
- if read_timeout is not None:
504
- if not read_timeout.is_initialized():
505
- raise RuntimeError("Timeout needs to be initialized")
513
+ if not read_timeout.is_initialized():
514
+ raise RuntimeError("Timeout needs to be initialized")
506
515
 
507
- _response = read_timeout.response()
516
+ _response = read_timeout.response()
508
517
 
509
- read_init_time = time.time()
510
- start_read_id = self._make_backend_request(Action.START_READ, _response)[0]
511
-
512
- if _response is None:
513
- # Wait indefinitely
514
- read_stop_timestamp = None
515
- else:
516
- # Wait for the response time + a bit more
517
- read_stop_timestamp = read_init_time + _response
518
+ read_init_time = time.time()
519
+ start_read_id = self._make_backend_request(Action.START_READ, _response)[0]
518
520
 
521
+ if _response is None:
522
+ # Wait indefinitely
523
+ read_stop_timestamp = None
519
524
  else:
520
- start_read_id = None
521
- read_init_time = None
525
+ # Wait for the response time + a bit more
526
+ read_stop_timestamp = read_init_time + _response
527
+
528
+ # else:
529
+ # start_read_id = None
530
+ # read_init_time = None
522
531
 
523
532
 
524
533
  while True:
@@ -542,12 +551,19 @@ class Adapter(ABC):
542
551
  break
543
552
  # Otherwise ignore it
544
553
 
545
-
546
554
  if output_signal is None:
547
555
  # TODO : Make read_timeout always Timeout, never None ?
548
556
  match read_timeout.action:
549
- case TimeoutAction.RETURN:
550
- return None
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
+ )
551
567
  case TimeoutAction.ERROR:
552
568
  raise TimeoutError(
553
569
  f"No response received from device within {read_timeout.response()} seconds"
@@ -558,97 +574,13 @@ class Adapter(ABC):
558
574
  else:
559
575
  return output_signal
560
576
 
561
-
562
- # Okay idea : Remove the start read and instead ask for the time of the backend.
563
- # Then we read whatever payload comes from the backend and compare that to the time
564
- # If it doesn't match our criteria, we trash it
565
- # When waiting for the backend payload, we wait +0.5s so make sure we received everything
566
- # This 0.5s could be changed if we're local or not by the way
567
-
568
-
569
- # # First, ask for the backend time, this is the official start of the read
570
- # backend_read_start_time = cast(NumberLike, self._make_backend_request(Action.GET_BACKEND_TIME)[0])
571
-
572
- # If not timeout is specified, use the default one
573
-
574
- # Calculate last_valid_timestamp, the limit at which a payload is not accepted anymore
575
- # Calculate the queue timeout (time for a response + small delay)
576
- #last_valid_timestamp = None
577
- # queue_timeout_timestamp = None
578
- # if read_timeout is not None:
579
- # response_delay = read_timeout.response()
580
- # else:
581
- # response_delay = None
582
-
583
- # if response_delay is not None:
584
- # #last_valid_timestamp = backend_read_start_time + response_delay
585
- # queue_timeout_timestamp = time.time() + response_delay + BACKEND_REQUEST_DEFAULT_TIMEOUT
586
-
587
- # output_signal : AdapterReadPayload | None
588
- # # This delay is given by the backend when a fragment is received. It basically says
589
- # # "I've received something, wait at most x seconds before raising an error"
590
- # read_init_end_delay : float | None = None
591
-
592
- # # Ready to read payloads
593
- # while True:
594
- # if read_init_end_delay is not None:
595
- # # Find the next timeout
596
- # queue_timeout = read_init_end_delay + 0.1 # TODO : Make this clean, this is the delay to let data arrive to the frontend
597
- # elif queue_timeout_timestamp is None:
598
- # queue_timeout = None
599
- # else:
600
- # queue_timeout = queue_timeout_timestamp - time.time()
601
- # if queue_timeout < 0:
602
- # queue_timeout = 0
603
-
604
- # try:
605
- # response = self._signal_queue.peek(block=True, timeout=queue_timeout)
606
- # signal = response[1]
607
-
608
- # if isinstance(signal, AdapterReadPayload):
609
- # if response_delay is not None and signal.response_timestamp - backend_read_start_time > response_delay:
610
- # # This signal happened after the max response time, act as if a timeout occured
611
- # # and do not pop it out of the queue
612
- # # TODO : Make _timeout always Timeout, never None ?
613
- # output_signal = None
614
- # break
615
-
616
- # if _scope == ReadScope.NEXT and signal.response_timestamp < backend_read_start_time:
617
- # # The payload happened before the read start
618
- # self._signal_queue.get()
619
- # continue
620
-
621
- # if response_delay is not None:
622
- # if signal.response_timestamp - backend_read_start_time > response_delay:
623
- # self._signal_queue.get()
624
- # output_signal = None
625
- # break
626
-
627
- # # Other wise the payload is valid
628
- # self._signal_queue.get()
629
- # output_signal = signal
630
- # break
631
- # elif isinstance(signal, AdapterReadInit):
632
- # read_init_end_delay = signal.end_delay
633
- # self._signal_queue.get()
634
- # elif isinstance(signal, AdapterDisconnected):
635
- # self._signal_queue.get()
636
-
637
-
638
- # except queue.Empty:
639
- # output_signal = None
640
- # break
641
-
642
577
  def read(
643
578
  self,
644
579
  timeout: Timeout | EllipsisType | None = ...,
645
- stop_condition: StopCondition | EllipsisType | None = ...,
580
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
646
581
  ) -> bytes:
647
- signal = self.read_detailed(timeout=timeout, stop_condition=stop_condition)
648
- if signal is None:
649
- return None
650
- else:
651
- return signal.data()
582
+ signal = self.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
583
+ return signal.data()
652
584
 
653
585
  def _cleanup(self) -> None:
654
586
  if self._init_ok and self.opened:
@@ -658,8 +590,8 @@ class Adapter(ABC):
658
590
  self,
659
591
  data: bytes | str,
660
592
  timeout: Timeout | EllipsisType | None = ...,
661
- stop_condition: StopCondition | EllipsisType | None = ...,
662
- ) -> tuple[bytes, AdapterReadPayload | None]:
593
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
594
+ ) -> AdapterReadPayload:
663
595
  """
664
596
  Shortcut function that combines
665
597
  - flush_read
@@ -668,21 +600,18 @@ class Adapter(ABC):
668
600
  """
669
601
  self.flushRead()
670
602
  self.write(data)
671
- return self.read_detailed(timeout=timeout, stop_condition=stop_condition)
603
+ return self.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
672
604
 
673
605
  def query(
674
606
  self,
675
607
  data: bytes | str,
676
608
  timeout: Timeout | EllipsisType | None = ...,
677
- stop_condition: StopCondition | EllipsisType | None = ...,
609
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
678
610
  ) -> bytes:
679
611
  signal = self.query_detailed(
680
- data=data, timeout=timeout, stop_condition=stop_condition
612
+ data=data, timeout=timeout, stop_conditions=stop_conditions
681
613
  )
682
- if signal is None:
683
- return None
684
- else:
685
- return signal.data()
614
+ return signal.data()
686
615
 
687
616
  def set_event_callback(self, callback: Callable[[AdapterSignal], None]) -> None:
688
617
  self.event_callback = callback
@@ -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)
@@ -260,7 +260,6 @@ class AdapterBackend(ABC):
260
260
  else:
261
261
  fragment_delta_t = float("nan")
262
262
  if fragment.data == b"":
263
- self._logger.debug('Empty data -> close')
264
263
  self.close()
265
264
  yield AdapterDisconnected()
266
265
  else:
@@ -301,12 +301,12 @@ class AdapterSession(threading.Thread):
301
301
  self._logger.error("Could not write, adapter is closed")
302
302
  case Action.PING:
303
303
  response_action, extra_arguments = Action.PING, ()
304
- case Action.SET_STOP_CONDITION:
304
+ case Action.SET_STOP_CONDITIONs:
305
305
  self._adapter.set_stop_conditions([
306
306
  stop_condition_to_backend(sc) for sc in request[1]
307
307
  ])
308
308
  response_action, extra_arguments = (
309
- Action.SET_STOP_CONDITION,
309
+ Action.SET_STOP_CONDITIONs,
310
310
  (),
311
311
  )
312
312
  case Action.FLUSHREAD:
@@ -69,8 +69,9 @@ class IPDescriptor(Descriptor):
69
69
 
70
70
  DETECTION_PATTERN = r"(\d+.\d+.\d+.\d+|[\w\.]+):\d+:(UDP|TCP)"
71
71
  address: str
72
+ transport: Transport
72
73
  port: int | None = None
73
- transport: Transport | None = None
74
+ #transport: Transport | None = None
74
75
 
75
76
  @staticmethod
76
77
  def from_string(string: str) -> "IPDescriptor":
@@ -78,7 +79,7 @@ class IPDescriptor(Descriptor):
78
79
  address = parts[0]
79
80
  port = int(parts[1])
80
81
  transport = IPDescriptor.Transport(parts[2])
81
- return IPDescriptor(address, port, transport)
82
+ return IPDescriptor(address, transport, port)
82
83
 
83
84
  def __str__(self) -> str:
84
85
  return f"{self.address}:{self.port}:{self.Transport(self.transport).value}"
@@ -140,6 +140,7 @@ class IPBackend(AdapterBackend):
140
140
 
141
141
  if fragment.data == b"":
142
142
  # Socket disconnected
143
+ self._logger.debug('## Socket disconnected')
143
144
  self.close()
144
145
 
145
146
  return fragment
@@ -6,7 +6,7 @@
6
6
 
7
7
  import time
8
8
 
9
- import serial # type: ignore
9
+ import serial
10
10
  from serial.serialutil import PortNotOpenError
11
11
 
12
12
  from syndesi.tools.backend_api import AdapterBackendStatus, Fragment
@@ -83,10 +83,10 @@ class SerialPortBackend(AdapterBackend):
83
83
  baudrate=self.descriptor.baudrate,
84
84
  rtscts=self._rts_cts,
85
85
  )
86
- elif not self._port.isOpen():
86
+ elif not self._port.isOpen(): # type: ignore
87
87
  self._port.open()
88
88
 
89
- if self._port.isOpen():
89
+ if self._port.isOpen(): # type: ignore
90
90
  self._logger.info(f"Adapter {self.descriptor} opened")
91
91
  self._status = AdapterBackendStatus.CONNECTED
92
92
  return True
@@ -112,7 +112,7 @@ class SerialPortBackend(AdapterBackend):
112
112
  return False
113
113
  else:
114
114
  if self._rts_cts: # Experimental
115
- self._port.setRTS(True) # type : ignore
115
+ self._port.setRTS(True) # type: ignore
116
116
  # TODO : Implement auto open
117
117
  # if self._status == AdapterBackendStatus.DISCONNECTED:
118
118
  # self.open()
@@ -230,7 +230,7 @@ class SerialPortBackend(AdapterBackend):
230
230
 
231
231
  def is_opened(self) -> bool:
232
232
  if self._port is not None:
233
- if self._port.isOpen(): # type : ignore
233
+ if self._port.isOpen(): # type: ignore
234
234
  return True
235
235
 
236
236
  return False
@@ -24,14 +24,14 @@ from .descriptors import VisaDescriptor
24
24
 
25
25
  # --- Typing-only imports so mypy knows pyvisa symbols without requiring it at runtime
26
26
  if TYPE_CHECKING:
27
- import pyvisa
28
- from pyvisa.resources import Resource
27
+ import pyvisa # type: ignore
28
+ from pyvisa.resources import Resource # type: ignore
29
29
 
30
30
  # --- Runtime optional import
31
31
  try:
32
32
  import pyvisa as _pyvisa_runtime
33
33
  except Exception:
34
- _pyvisa_runtime = None # type:ignore
34
+ _pyvisa_runtime = None
35
35
 
36
36
  pyvisa: ModuleType | None = _pyvisa_runtime # type: ignore
37
37
 
@@ -105,8 +105,8 @@ class VisaBackend(AdapterBackend):
105
105
 
106
106
  if self._status == AdapterBackendStatus.DISCONNECTED:
107
107
  # These attributes exist on pyvisa resources
108
- self._inst.write_termination = "" # type: ignore[attr-defined]
109
- self._inst.read_termination = None # type: ignore[attr-defined]
108
+ self._inst.write_termination = ""
109
+ self._inst.read_termination = None
110
110
 
111
111
  self._inst_lock = threading.Lock()
112
112
  self._status = AdapterBackendStatus.CONNECTED
@@ -136,7 +136,7 @@ class VisaBackend(AdapterBackend):
136
136
  super().write(data)
137
137
  with self._inst_lock:
138
138
  if self._inst is not None:
139
- self._inst.write_raw(data) # type: ignore[attr-defined]
139
+ self._inst.write_raw(data)
140
140
  return True
141
141
 
142
142
  def _socket_read(self) -> Fragment:
@@ -169,7 +169,7 @@ class VisaBackend(AdapterBackend):
169
169
  try:
170
170
  while True:
171
171
  # Read up to an error
172
- payload += inst.read_bytes(1) # type: ignore[attr-defined]
172
+ payload += inst.read_bytes(1)
173
173
  inst.timeout = 0
174
174
  except pyvisa.VisaIOError:
175
175
  # Timeout
@@ -28,9 +28,9 @@ class IP(Adapter):
28
28
  self,
29
29
  address: str,
30
30
  port: int | None = None,
31
- transport: str = IPDescriptor.Transport.TCP.value,
31
+ transport: str = DEFAULT_PROTOCOL.value,
32
32
  timeout: Timeout | NumberLike | None | EllipsisType = ...,
33
- stop_conditions: StopCondition | None | EllipsisType | list = ...,
33
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
34
34
  alias: str = "",
35
35
  encoding: str = "utf-8",
36
36
  event_callback: Callable[[AdapterSignal], None] | None = None,
@@ -63,11 +63,7 @@ class IP(Adapter):
63
63
  descriptor=IPDescriptor(
64
64
  address=address,
65
65
  port=port,
66
- transport=(
67
- IPDescriptor.Transport(transport.upper())
68
- if transport is not None
69
- else self.DEFAULT_PROTOCOL
70
- ),
66
+ transport=IPDescriptor.Transport(transport.upper())
71
67
  ),
72
68
  alias=alias,
73
69
  timeout=timeout,
@@ -80,8 +76,8 @@ class IP(Adapter):
80
76
  )
81
77
  self.descriptor: IPDescriptor
82
78
 
83
- if self.descriptor.transport is None:
84
- self._logger.info(f"Setting up {self.descriptor.transport.value} IP adapter")
79
+ #if self.descriptor.transport is not None:
80
+ self._logger.info(f"Setting up {self.descriptor.transport.value} IP adapter")
85
81
 
86
82
  self.set_default_timeout(self._default_timeout())
87
83
 
@@ -102,7 +98,7 @@ class IP(Adapter):
102
98
  if self.descriptor.port is None:
103
99
  self.descriptor.port = port
104
100
 
105
- def set_default_transport(self, transport : str | IPDescriptor.Transport):
101
+ def set_default_transport(self, transport : str | IPDescriptor.Transport) -> None:
106
102
  """
107
103
  Sets the default IP transport protocol
108
104
 
@@ -13,7 +13,7 @@ from ..tools.types import NumberLike, is_number
13
13
 
14
14
  class TimeoutAction(Enum):
15
15
  ERROR = "error"
16
- RETURN = "return"
16
+ RETURN_EMPTY = "return_empty"
17
17
 
18
18
 
19
19
  class IsInitialized(Protocol):
@@ -20,7 +20,7 @@ class Visa(Adapter):
20
20
  self,
21
21
  descriptor: str,
22
22
  alias: str = "",
23
- stop_condition: StopCondition | None | EllipsisType = ...,
23
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
24
24
  timeout: None | float | Timeout | EllipsisType = ...,
25
25
  encoding: str = "utf-8",
26
26
  event_callback: Callable[[AdapterSignal], None] | None = None,
@@ -30,7 +30,7 @@ class Visa(Adapter):
30
30
  super().__init__(
31
31
  VisaDescriptor.from_string(descriptor),
32
32
  alias=alias,
33
- stop_conditions=stop_condition,
33
+ stop_conditions=stop_conditions,
34
34
  timeout=timeout,
35
35
  encoding=encoding,
36
36
  event_callback=event_callback,
@@ -110,10 +110,8 @@ class Delimited(Protocol):
110
110
  def query(
111
111
  self,
112
112
  data: str,
113
- timeout: Timeout | None | EllipsisType = ...,
114
- decode: bool = True,
115
- full_output: bool = False,
116
- ) -> str | tuple[str, AdapterSignal]:
113
+ timeout: Timeout | None | EllipsisType = ...
114
+ ) -> str:
117
115
  """
118
116
  Writes then reads from the device and return the result
119
117
 
@@ -130,13 +128,13 @@ class Delimited(Protocol):
130
128
  """
131
129
  self._adapter.flushRead()
132
130
  self.write(data)
133
- return self.read(timeout=timeout, decode=decode, full_output=full_output)
131
+ return self.read(timeout=timeout)
134
132
 
135
133
  def read_raw(
136
134
  self,
137
135
  timeout: Timeout | None | EllipsisType = ...,
138
- stop_condition: StopCondition | None | EllipsisType = ...,
139
- ) -> tuple[bytes, AdapterReadPayload | None]:
136
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
137
+ ) -> bytes:
140
138
  """
141
139
  Reads command and formats it as a str
142
140
 
@@ -151,30 +149,27 @@ class Delimited(Protocol):
151
149
  """
152
150
 
153
151
  # Send up to the termination
154
- raw_data, signal = self._adapter.read_detailed(
155
- timeout=timeout, stop_condition=stop_condition
152
+ signal = self._adapter.read_detailed(
153
+ timeout=timeout, stop_conditions=stop_conditions
156
154
  )
157
-
158
- return raw_data, signal
155
+ return signal.data()
159
156
 
160
157
  def read_detailed(
161
158
  self,
162
159
  timeout: Timeout | None | EllipsisType = ...,
163
- stop_condition: StopCondition | None | EllipsisType = ...,
164
- ) -> tuple[str, AdapterReadPayload | None]:
165
- raw_data, signal = self.read_raw(timeout=timeout, stop_condition=stop_condition)
166
-
167
- data_out = self._decode(raw_data)
168
- return data_out, signal
160
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...
161
+ ) -> AdapterReadPayload:
162
+ signal = self._adapter.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
163
+ return signal
169
164
 
170
165
  def read(
171
166
  self,
172
167
  timeout: Timeout | None | EllipsisType = ...,
173
- stop_condition: StopCondition | None | EllipsisType = ...,
174
- decode: bool = True,
175
- full_output: bool = False,
168
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...
176
169
  ) -> str:
177
- return self.read_detailed(timeout=timeout, stop_condition=stop_condition)[0]
170
+ signal = self.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
171
+ return self._decode(signal.data())
172
+
178
173
 
179
174
  def _decode(self, data: bytes) -> str:
180
175
  try:
@@ -14,7 +14,7 @@ from unittest.mock import DEFAULT
14
14
  from ..adapters.adapter import Adapter
15
15
  from ..adapters.ip import IP
16
16
  from ..adapters.serialport import SerialPort
17
- from ..adapters.stop_condition import Length
17
+ from ..adapters.stop_condition import Continuation, Length
18
18
  from ..adapters.timeout import Timeout
19
19
  from .protocol import Protocol
20
20
 
@@ -328,10 +328,12 @@ class Modbus(Protocol):
328
328
  def _error_code(self, response: bytes) -> int:
329
329
  return response[1]
330
330
 
331
- def _parse_pdu(self, _pdu: bytes) -> bytes:
331
+ def _parse_pdu(self, _pdu: bytes | None) -> bytes:
332
332
  """
333
333
  Return data from PDU
334
334
  """
335
+ if _pdu is None:
336
+ raise RuntimeError("Failed to read modbus data")
335
337
  if self._modbus_type == ModbusType.TCP:
336
338
  # Return raw data
337
339
  # data = _pdu
@@ -391,12 +393,13 @@ class Modbus(Protocol):
391
393
  )
392
394
 
393
395
  n_coil_bytes = ceil(number_of_coils / 8)
394
- pdu: bytes = self._adapter.query(
396
+ pdu: bytes | None = self._adapter.query(
395
397
  self._make_pdu(query),
396
398
  # timeout=Timeout(continuation=1),
397
- stop_condition=Length(
398
- self._length(n_coil_bytes + 2)
399
- ), # TODO : convert to multiple stop conditions here
399
+ stop_conditions=[
400
+ Length(self._length(n_coil_bytes + 2)),
401
+ Continuation(time=1)
402
+ ] # TODO : convert to multiple stop conditions here
400
403
  )
401
404
  response = self._parse_pdu(pdu)
402
405
  self._raise_if_error(response, exception_codes=EXCEPTIONS)
@@ -465,7 +468,7 @@ class Modbus(Protocol):
465
468
  response = self._parse_pdu(
466
469
  self._adapter.query(
467
470
  self._make_pdu(query),
468
- stop_condition=Length(self._length(byte_count + 2)),
471
+ stop_conditions=Length(self._length(byte_count + 2)),
469
472
  )
470
473
  )
471
474
  self._raise_if_error(response, exception_codes=EXCEPTIONS)
@@ -515,7 +518,7 @@ class Modbus(Protocol):
515
518
  response = self._parse_pdu(
516
519
  self._adapter.query(
517
520
  self._make_pdu(query),
518
- stop_condition=Length(self._length(2 + number_of_registers * 2)),
521
+ stop_conditions=Length(self._length(2 + number_of_registers * 2)),
519
522
  )
520
523
  )
521
524
  self._raise_if_error(response, exception_codes=EXCEPTIONS)
@@ -769,7 +772,7 @@ class Modbus(Protocol):
769
772
  )
770
773
  response = self._parse_pdu(
771
774
  self._adapter.query(
772
- self._make_pdu(query), stop_condition=Length(self._length(len(query)))
775
+ self._make_pdu(query), stop_conditions=Length(self._length(len(query)))
773
776
  )
774
777
  )
775
778
  self._raise_if_error(response, EXCEPTIONS)
@@ -805,7 +808,7 @@ class Modbus(Protocol):
805
808
  )
806
809
  response = self._parse_pdu(
807
810
  self._adapter.query(
808
- self._make_pdu(query), stop_condition=Length(self._length(len(query)))
811
+ self._make_pdu(query), stop_conditions=Length(self._length(len(query)))
809
812
  )
810
813
  )
811
814
  self._raise_if_error(response, EXCEPTIONS)
@@ -1198,7 +1201,7 @@ class Modbus(Protocol):
1198
1201
  )
1199
1202
  response = self._parse_pdu(
1200
1203
  self._adapter.query(
1201
- self._make_pdu(query), stop_condition=Length(self._length(5))
1204
+ self._make_pdu(query), stop_conditions=Length(self._length(5))
1202
1205
  )
1203
1206
  )
1204
1207
  self._raise_if_error(response, EXCEPTIONS)
@@ -1250,7 +1253,7 @@ class Modbus(Protocol):
1250
1253
  )
1251
1254
  response = self._parse_pdu(
1252
1255
  self._adapter.query(
1253
- self._make_pdu(query), stop_condition=Length(self._length(5))
1256
+ self._make_pdu(query), stop_conditions=Length(self._length(5))
1254
1257
  )
1255
1258
  )
1256
1259
  self._raise_if_error(response, EXCEPTIONS)
@@ -1451,7 +1454,7 @@ class Modbus(Protocol):
1451
1454
  )
1452
1455
  response = self._parse_pdu(
1453
1456
  self._adapter.query(
1454
- self._make_pdu(query), stop_condition=Length(self._length(len(query)))
1457
+ self._make_pdu(query), stop_conditions=Length(self._length(len(query)))
1455
1458
  )
1456
1459
  )
1457
1460
  self._raise_if_error(response, EXCEPTIONS)
@@ -1521,7 +1524,7 @@ class Modbus(Protocol):
1521
1524
  response = self._parse_pdu(
1522
1525
  self._adapter.query(
1523
1526
  self._make_pdu(query),
1524
- stop_condition=Length(self._length(2 + number_of_read_registers * 2)),
1527
+ stop_conditions=Length(self._length(2 + number_of_read_registers * 2)),
1525
1528
  )
1526
1529
  )
1527
1530
  self._raise_if_error(response, EXCEPTIONS)
@@ -49,33 +49,37 @@ class Raw(Protocol):
49
49
  self,
50
50
  data: bytes,
51
51
  timeout: Timeout | None | EllipsisType = ...,
52
- stop_condition: StopCondition | None | EllipsisType = ...,
53
- full_output: bool = False,
54
- ) -> bytes | tuple[bytes, AdapterSignal]:
52
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
53
+ ) -> bytes:
55
54
  self._adapter.flushRead()
56
55
  self.write(data)
57
- return self.read(
58
- timeout=timeout,
59
- stop_condition=stop_condition,
60
- full_output=full_output,
61
- )
56
+ return self.read(timeout=timeout, stop_conditions=stop_conditions)
57
+
58
+ def query_detailed(
59
+ self,
60
+ data: bytes,
61
+ timeout: Timeout | None | EllipsisType = ...,
62
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
63
+ ) -> AdapterReadPayload:
64
+ self._adapter.flushRead()
65
+ self.write(data)
66
+ return self.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
62
67
 
63
68
  def read(
64
69
  self,
65
70
  timeout: Timeout | None | EllipsisType = ...,
66
- stop_condition: StopCondition | None | EllipsisType = ...,
67
- full_output: bool = False,
68
- ) -> bytes | tuple[bytes, AdapterSignal]:
69
-
70
- return self.read_detailed(timeout=timeout, stop_condition=stop_condition)[0]
71
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
72
+ ) -> bytes:
73
+ signal = self.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
74
+ return signal.data()
71
75
 
72
76
  def read_detailed(
73
77
  self,
74
78
  timeout: Timeout | None | EllipsisType = ...,
75
- stop_condition: StopCondition | None | EllipsisType = ...,
76
- ) -> tuple[bytes, AdapterSignal | None]:
79
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
80
+ ) -> AdapterReadPayload:
77
81
  return self._adapter.read_detailed(
78
- timeout=timeout, stop_condition=stop_condition
82
+ timeout=timeout, stop_conditions=stop_conditions
79
83
  )
80
84
 
81
85
  def _on_data_ready_event(self, data: AdapterReadPayload) -> None:
@@ -5,7 +5,7 @@
5
5
 
6
6
  from types import EllipsisType
7
7
 
8
- from syndesi.adapters.backend.adapter_backend import AdapterSignal
8
+ from syndesi.adapters.backend.adapter_backend import AdapterReadPayload, AdapterSignal
9
9
 
10
10
  from ..adapters.adapter import Adapter
11
11
  from ..adapters.ip import IP
@@ -105,37 +105,39 @@ class SCPI(Protocol):
105
105
  self,
106
106
  command: str,
107
107
  timeout: Timeout | None | EllipsisType = ...,
108
- stop_condition: StopCondition | None | EllipsisType = ...,
108
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
109
109
  ) -> str:
110
110
  self._adapter.flushRead()
111
111
  self.write(command)
112
112
 
113
- return self.read(timeout=timeout, stop_condition=stop_condition)
113
+ return self.read(timeout=timeout, stop_conditions=stop_conditions)
114
114
 
115
115
  def read_raw(
116
116
  self,
117
117
  timeout: Timeout | None | EllipsisType = ...,
118
- stop_condition: StopCondition | None | EllipsisType = ...,
119
- ) -> tuple[bytes, AdapterSignal | None]:
120
- return self._adapter.read_detailed(
118
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
119
+ ) -> bytes:
120
+ signal = self.read_detailed(
121
121
  timeout=timeout,
122
- stop_condition=stop_condition,
122
+ stop_conditions=stop_conditions,
123
123
  )
124
+ return signal.data()
124
125
 
125
126
  def read_detailed(
126
127
  self,
127
128
  timeout: Timeout | None | EllipsisType = ...,
128
- stop_condition: StopCondition | None | EllipsisType = ...,
129
- ) -> tuple[str, AdapterSignal | None]:
129
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
130
+ ) -> AdapterReadPayload:
130
131
 
131
- raw_data, signal = self.read_raw(timeout=timeout, stop_condition=stop_condition)
132
- data_out = self._unformatCommand(self._from_bytes(raw_data))
133
-
134
- return data_out, signal
132
+ signal = self._adapter.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
133
+ return signal
135
134
 
136
135
  def read(
137
136
  self,
138
137
  timeout: Timeout | None | EllipsisType = ...,
139
- stop_condition: StopCondition | None | EllipsisType = ...,
138
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
140
139
  ) -> str:
141
- return self.read_detailed(timeout=timeout, stop_condition=stop_condition)[0]
140
+ signal = self.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
141
+ raw_data = signal.data()
142
+ return self._unformatCommand(self._from_bytes(raw_data))
143
+
@@ -34,7 +34,7 @@ class Action(Enum):
34
34
  #FORCE_CLOSE = "force_close" # (descriptor,) -> ()
35
35
  WRITE = "write" # (descriptor,data) -> ()
36
36
  READ = "read" # (descriptor,full_output,temporary_timeout,temporary_stop_condition) -> (data,metrics)
37
- SET_STOP_CONDITION = "set_stop_condition" # (descriptor,stop_condition)
37
+ SET_STOP_CONDITIONs = "set_stop_condition" # (descriptor,stop_condition)
38
38
  FLUSHREAD = "flushread"
39
39
  START_READ = "start_read" # Start a read (descriptor,response_time)
40
40
  RESPONSE_TIMEOUT = "response_timeout"
@@ -1,3 +1,3 @@
1
- __version__ = "0.4.0"
1
+ __version__ = "0.4.1"
2
2
  NAME = "Syndesi"
3
3
  AUTHOR = "Sébastien Deriaz"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syndesi
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Syndesi
5
5
  Author-email: Sébastien Deriaz <sebastien.deriaz1@gmail.com>
6
6
  License: GPL
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes