syndesi 0.4.0__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.
Files changed (38) hide show
  1. syndesi/adapters/adapter.py +91 -158
  2. syndesi/adapters/auto.py +1 -1
  3. syndesi/adapters/backend/adapter_backend.py +54 -37
  4. syndesi/adapters/backend/adapter_session.py +26 -27
  5. syndesi/adapters/backend/backend_status.py +0 -0
  6. syndesi/adapters/backend/backend_tools.py +1 -1
  7. syndesi/adapters/backend/descriptors.py +3 -2
  8. syndesi/adapters/backend/ip_backend.py +1 -0
  9. syndesi/adapters/backend/serialport_backend.py +9 -10
  10. syndesi/adapters/backend/stop_condition_backend.py +47 -26
  11. syndesi/adapters/backend/visa_backend.py +7 -7
  12. syndesi/adapters/ip.py +6 -10
  13. syndesi/adapters/stop_condition.py +10 -83
  14. syndesi/adapters/timeout.py +3 -30
  15. syndesi/adapters/visa.py +2 -2
  16. syndesi/cli/backend_status.py +7 -9
  17. syndesi/cli/console.py +1 -54
  18. syndesi/cli/shell.py +1 -14
  19. syndesi/cli/shell_tools.py +0 -5
  20. syndesi/protocols/delimited.py +17 -37
  21. syndesi/protocols/modbus.py +17 -14
  22. syndesi/protocols/raw.py +20 -16
  23. syndesi/protocols/scpi.py +18 -15
  24. syndesi/scripts/syndesi.py +1 -3
  25. syndesi/tools/backend_api.py +5 -38
  26. syndesi/tools/backend_logger.py +0 -1
  27. syndesi/tools/errors.py +4 -5
  28. syndesi/tools/log.py +0 -88
  29. syndesi/tools/types.py +0 -44
  30. syndesi/version.py +1 -1
  31. syndesi-0.4.2.dist-info/METADATA +96 -0
  32. syndesi-0.4.2.dist-info/RECORD +60 -0
  33. syndesi-0.4.0.dist-info/METADATA +0 -123
  34. syndesi-0.4.0.dist-info/RECORD +0 -59
  35. {syndesi-0.4.0.dist-info → syndesi-0.4.2.dist-info}/WHEEL +0 -0
  36. {syndesi-0.4.0.dist-info → syndesi-0.4.2.dist-info}/entry_points.txt +0 -0
  37. {syndesi-0.4.0.dist-info → syndesi-0.4.2.dist-info}/licenses/LICENSE +0 -0
  38. {syndesi-0.4.0.dist-info → syndesi-0.4.2.dist-info}/top_level.txt +0 -0
@@ -8,12 +8,9 @@ from typing import Any, Protocol
8
8
 
9
9
  from ..tools.types import NumberLike, is_number
10
10
 
11
- # from .backend.timeout import TimeoutAction, JsonKey
12
-
13
-
14
11
  class TimeoutAction(Enum):
15
12
  ERROR = "error"
16
- RETURN = "return"
13
+ RETURN_EMPTY = "return_empty"
17
14
 
18
15
 
19
16
  class IsInitialized(Protocol):
@@ -78,10 +75,9 @@ class Timeout:
78
75
  return None
79
76
  else:
80
77
  return self._response
81
-
82
- def is_initialized(self) -> bool:
83
- return not self._response is Ellipsis
84
78
 
79
+ def is_initialized(self) -> bool:
80
+ return self._response is not Ellipsis
85
81
 
86
82
 
87
83
  def any_to_timeout(value: Any) -> Timeout:
@@ -93,26 +89,3 @@ def any_to_timeout(value: Any) -> Timeout:
93
89
  return value
94
90
  else:
95
91
  raise ValueError(f"Could not convert {value} to Timeout")
96
-
97
-
98
- # class TimeoutException(Exception):
99
- # def __init__(self, value: NumberLike, limit: NumberLike) -> None:
100
- # super().__init__()
101
- # self._value = value
102
- # self._limit = limit
103
-
104
- # def __str__(self) -> str:
105
- # try:
106
- # value_string = f"{self._value * 1e3:.3f}ms"
107
- # except (ValueError, TypeError):
108
- # value_string = "not received"
109
-
110
- # try:
111
- # limit_string = f"{self._limit * 1e3:.3f}ms"
112
- # except (ValueError, TypeError):
113
- # limit_string = "not received"
114
-
115
- # return f"{value_string} / {limit_string}"
116
-
117
- # def __repr__(self) -> str:
118
- # return self.__str__()
syndesi/adapters/visa.py CHANGED
@@ -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,
@@ -95,8 +95,6 @@ class BackendStatus:
95
95
  # Buffer for last N_CONSOLE_LINES log messages
96
96
  self.console_lines: list[str] = []
97
97
  self.console_lock = threading.Lock() # Lock for thread safety
98
- # self._backend_logger = BackendLogger() # use_queue=True)
99
- # self._backend_logger.start()
100
98
  log_manager.enable_backend_logging()
101
99
  self._start_time = time.time()
102
100
  self.conn: NamedConnection | None = None
@@ -155,7 +153,9 @@ class BackendStatus:
155
153
  adapter_table = Table(
156
154
  "", box=None, caption_justify="right"
157
155
  )
158
- snapshot: dict[str, tuple[bool, list[str]]] = event[1]
156
+ snapshot: dict[str, tuple[bool, list[str]]] = event[
157
+ 1
158
+ ]
159
159
  unique_clients: set[str] = set()
160
160
  for _, (_, adapter_clients) in snapshot.items():
161
161
  unique_clients |= set(adapter_clients)
@@ -191,7 +191,9 @@ class BackendStatus:
191
191
  pad_edge=False,
192
192
  )
193
193
  # Update monitoring connections
194
- monitoring_response: list[tuple[str, str]] = event[1]
194
+ monitoring_response: list[tuple[str, str]] = event[
195
+ 1
196
+ ]
195
197
 
196
198
  for connection, desc in monitoring_response:
197
199
  if (
@@ -206,10 +208,6 @@ class BackendStatus:
206
208
  monitoring_connections.add_row(
207
209
  f"[{style[0]}]{connection}[/] [{style[1]}]({desc})[/]"
208
210
  )
209
- # if connection == status_conn:
210
- # logger_connections.add_row(f"[grey50]{connection}[/]")
211
- # else:
212
- # logger_connections.add_row(c_string)
213
211
  for _ in range(
214
212
  self.CONNECTIONS_MIN_HEIGHT
215
213
  - len(monitoring_response)
@@ -273,4 +271,4 @@ class BackendStatus:
273
271
  self.console_lines.append(formated_text)
274
272
  if len(self.console_lines) > self._n_console_lines:
275
273
  self.console_lines = self.console_lines[-self._n_console_lines :]
276
- self._new_console_line_w.send(b'\x00')
274
+ self._new_console_line_w.send(b"\x00")
syndesi/cli/console.py CHANGED
@@ -222,60 +222,7 @@ class Shell:
222
222
  FormattedText([(f"class:{style.value}", text)]), style=self._toolkit_styles
223
223
  )
224
224
 
225
- # def ask(self, question: str):
226
- # loop = self.session.app.loop
227
- # fut = asyncio.run_coroutine_threadsafe(self._ask_on_app_loop(question), loop)
228
- # return fut.result()
229
-
230
- # async def _ask_on_app_loop(self, question: str):
231
- # def _blocking_prompt():
232
- # temp_session = prompt_toolkit.PromptSession()
233
- # return temp_session.prompt(question)
234
- # # Or just: return input(question) if styling isn't needed
235
-
236
- # return await prompt_toolkit.application.run_in_terminal(_blocking_prompt)
237
-
238
- # def ask(self, question: str):
239
- # loop = self.session.app.loop
240
- # fut = asyncio.run_coroutine_threadsafe(self._ask_dialog(question), loop)
241
- # return fut.result()
242
-
243
- # async def _ask_dialog(self, question: str):
244
- # dlg = yes_no_dialog(title="Adapter Disconnected", text=question)
245
- # return await dlg.run_async()
246
-
247
225
  def ask(self, question: str, callback: Callable[[str], None]) -> None:
248
226
  self.ask_event.set()
249
227
  self.ask_callback = callback
250
- self.reprompt(question)
251
-
252
- # loop = self.session.app.loop
253
- # fut = asyncio.run_coroutine_threadsafe(self._ask_inline(question), loop)
254
- # return fut.result()
255
-
256
- # async def _ask_inline(self, question: str) -> bool:
257
- # from prompt_toolkit.patch_stdout import patch_stdout
258
- # from prompt_toolkit.shortcuts import prompt
259
-
260
- # # patch_stdout ensures print() doesn't mess up your REPL display
261
- # with patch_stdout():
262
- # ans = await prompt(f"{question} (y/n) ").strip().lower()
263
- # return ans.startswith("y")
264
-
265
- # def ask(self, question: str):
266
- # fut = asyncio.run_coroutine_threadsafe(
267
- # self._ask_on_app_loop(question), self.session.app.loop
268
- # )
269
- # answer = fut.result() # blocks THIS thread until user answers
270
- # return answer
271
-
272
- # # on your shell object (runs on the app loop):
273
- # async def _ask_on_app_loop(self, question):
274
- # def _blocking_prompt():
275
- # # Use your existing shell ask/prompt; it runs while the UI is suspended.
276
- # # If you have a PromptSession: return self.shell.session.prompt(...)
277
- # return self.session.prompt(question)
278
-
279
- # self.session.app.run
280
- # # Suspends rendering & CPR handling safely, runs in a thread pool.
281
- # return await prompt_toolkit.application.run_in_terminal(_blocking_prompt)
228
+ self.reprompt(question)
syndesi/cli/shell.py CHANGED
@@ -173,7 +173,7 @@ class AdapterShell:
173
173
  backend_port=args.backend_port,
174
174
  )
175
175
 
176
- self.adapter.set_default_timeout(Timeout(action="return"))
176
+ self.adapter.set_default_timeout(Timeout(action="return_empty"))
177
177
 
178
178
  # Add the protocol
179
179
  _format = Format(args.format)
@@ -205,13 +205,6 @@ class AdapterShell:
205
205
  def run(self) -> None:
206
206
  # try:
207
207
  self.adapter.open()
208
- # except Exception: # TODO : Change this to a suitable exception
209
- # self.shell.print(
210
- # f"Failed to open adapter {self.adapter.descriptor}",
211
- # style=Shell.Style.ERROR,
212
- # )
213
- # self.adapter.close() # TODO : Maybe force here ?
214
- # else:
215
208
  self.shell.print(
216
209
  f"Opened adapter {self.adapter.descriptor}", style=Shell.Style.NOTE
217
210
  )
@@ -227,12 +220,6 @@ class AdapterShell:
227
220
  if answer.lower() == "y":
228
221
  # try:
229
222
  self._protocol.open()
230
- # except Exception: # TODO : Change this
231
- # self.shell.print(
232
- # "Failed to open adapter", style=Shell.Style.WARNING
233
- # )
234
- # else:
235
- # self.shell.print("Adapter opened", style=Shell.Style.NOTE)
236
223
  else:
237
224
  # Set the stop flag, exit will be effective on reprompt
238
225
  self.shell.stop()
@@ -2,11 +2,6 @@
2
2
  # Author : Sébastien Deriaz
3
3
  # License : GPL
4
4
 
5
- import inspect
6
- from collections.abc import Callable, Iterable
7
- from dataclasses import dataclass
8
- from typing import Any
9
-
10
5
 
11
6
  # @dataclass
12
7
  # class CommandSpec:
@@ -107,13 +107,7 @@ class Delimited(Protocol):
107
107
  command = self._format_command(command)
108
108
  self._adapter.write(self._to_bytes(command))
109
109
 
110
- def query(
111
- self,
112
- data: str,
113
- timeout: Timeout | None | EllipsisType = ...,
114
- decode: bool = True,
115
- full_output: bool = False,
116
- ) -> str | tuple[str, AdapterSignal]:
110
+ def query(self, data: str, timeout: Timeout | None | EllipsisType = ...) -> str:
117
111
  """
118
112
  Writes then reads from the device and return the result
119
113
 
@@ -130,13 +124,13 @@ class Delimited(Protocol):
130
124
  """
131
125
  self._adapter.flushRead()
132
126
  self.write(data)
133
- return self.read(timeout=timeout, decode=decode, full_output=full_output)
127
+ return self.read(timeout=timeout)
134
128
 
135
129
  def read_raw(
136
130
  self,
137
131
  timeout: Timeout | None | EllipsisType = ...,
138
- stop_condition: StopCondition | None | EllipsisType = ...,
139
- ) -> tuple[bytes, AdapterReadPayload | None]:
132
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
133
+ ) -> bytes:
140
134
  """
141
135
  Reads command and formats it as a str
142
136
 
@@ -151,30 +145,28 @@ class Delimited(Protocol):
151
145
  """
152
146
 
153
147
  # Send up to the termination
154
- raw_data, signal = self._adapter.read_detailed(
155
- timeout=timeout, stop_condition=stop_condition
148
+ signal = self._adapter.read_detailed(
149
+ timeout=timeout, stop_conditions=stop_conditions
156
150
  )
157
-
158
- return raw_data, signal
151
+ return signal.data()
159
152
 
160
153
  def read_detailed(
161
154
  self,
162
155
  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
156
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
157
+ ) -> AdapterReadPayload:
158
+ signal = self._adapter.read_detailed(
159
+ timeout=timeout, stop_conditions=stop_conditions
160
+ )
161
+ return signal
169
162
 
170
163
  def read(
171
164
  self,
172
165
  timeout: Timeout | None | EllipsisType = ...,
173
- stop_condition: StopCondition | None | EllipsisType = ...,
174
- decode: bool = True,
175
- full_output: bool = False,
166
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
176
167
  ) -> str:
177
- return self.read_detailed(timeout=timeout, stop_condition=stop_condition)[0]
168
+ signal = self.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
169
+ return self._decode(signal.data())
178
170
 
179
171
  def _decode(self, data: bytes) -> str:
180
172
  try:
@@ -188,16 +180,4 @@ class Delimited(Protocol):
188
180
  # Add the termination back in since it was removed by the adapter
189
181
  data_string += self._receive_termination
190
182
 
191
- return data_string
192
-
193
- # @overload
194
- # def _append_missing_termination(self, data : str) -> str: ...
195
- # @overload
196
- # def _append_missing_termination(self, data : bytes) -> bytes: ...
197
- # def _append_missing_termination(self, data : Union[str, bytes]) -> Union[str, bytes]:
198
- # if isinstance(data, str):
199
- # return data + self._receive_termination
200
- # elif isinstance(data, bytes): # type bytes
201
- # return data + self._receive_termination.encode(self._encoding)
202
- # else:
203
- # raise ValueError(f"Couldn't append termination do {data}")
183
+ return data_string
@@ -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)
syndesi/protocols/raw.py CHANGED
@@ -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)
62
57
 
63
- def read(
58
+ def query_detailed(
64
59
  self,
60
+ data: bytes,
65
61
  timeout: Timeout | None | EllipsisType = ...,
66
- stop_condition: StopCondition | None | EllipsisType = ...,
67
- full_output: bool = False,
68
- ) -> bytes | tuple[bytes, AdapterSignal]:
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)
69
67
 
70
- return self.read_detailed(timeout=timeout, stop_condition=stop_condition)[0]
68
+ def read(
69
+ self,
70
+ timeout: Timeout | None | EllipsisType = ...,
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:
syndesi/protocols/scpi.py CHANGED
@@ -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
9
9
 
10
10
  from ..adapters.adapter import Adapter
11
11
  from ..adapters.ip import IP
@@ -105,37 +105,40 @@ 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(
133
+ timeout=timeout, stop_conditions=stop_conditions
134
+ )
135
+ return signal
135
136
 
136
137
  def read(
137
138
  self,
138
139
  timeout: Timeout | None | EllipsisType = ...,
139
- stop_condition: StopCondition | None | EllipsisType = ...,
140
+ stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
140
141
  ) -> str:
141
- return self.read_detailed(timeout=timeout, stop_condition=stop_condition)[0]
142
+ signal = self.read_detailed(timeout=timeout, stop_conditions=stop_conditions)
143
+ raw_data = signal.data()
144
+ return self._unformatCommand(self._from_bytes(raw_data))
@@ -22,9 +22,7 @@ def main() -> None:
22
22
  prog="syndesi", description="Syndesi command line tool", epilog=""
23
23
  )
24
24
 
25
- parser.add_argument(
26
- "--version", action="version", version=f"%(prog)s {__version__}"
27
- )
25
+ parser.add_argument("--version", action="version", version=f"Syndesi {__version__}")
28
26
  parser.add_argument("-v", "--verbose", action="store_true")
29
27
  parser.add_argument(
30
28
  "command",
@@ -26,25 +26,22 @@ default_host = LOCALHOST
26
26
 
27
27
  EXTRA_BUFFER_RESPONSE_TIME = 1
28
28
 
29
+
29
30
  class Action(Enum):
30
31
  # All adapters
31
32
  SELECT_ADAPTER = "select"
32
33
  OPEN = "open" # (descriptor,stop_condition) -> ()
33
- CLOSE = "close" # (descriptor,force) -> ()
34
- #FORCE_CLOSE = "force_close" # (descriptor,) -> ()
34
+ CLOSE = "close" # (descriptor,force) -> ()
35
+ # FORCE_CLOSE = "force_close" # (descriptor,) -> ()
35
36
  WRITE = "write" # (descriptor,data) -> ()
36
37
  READ = "read" # (descriptor,full_output,temporary_timeout,temporary_stop_condition) -> (data,metrics)
37
- SET_STOP_CONDITION = "set_stop_condition" # (descriptor,stop_condition)
38
+ SET_STOP_CONDITIONs = "set_stop_condition" # (descriptor,stop_condition)
38
39
  FLUSHREAD = "flushread"
39
40
  START_READ = "start_read" # Start a read (descriptor,response_time)
40
41
  RESPONSE_TIMEOUT = "response_timeout"
41
- #GET_BACKEND_TIME = "get_time"
42
42
 
43
43
  # Signal
44
44
  ADAPTER_SIGNAL = "adapter_signal"
45
- #ADAPTER_EVENT_DATA_READY = "event_adapter_data_ready"
46
- #ADAPTER_EVENT_DISCONNECTED = "event_adapter_disconnected"
47
- #ADAPTER_EVENT_READ_INIT = "event_adapter_read_init"
48
45
 
49
46
  # Other
50
47
  SET_ROLE_ADAPTER = (
@@ -72,22 +69,15 @@ class Action(Enum):
72
69
  ERROR_FAILED_TO_OPEN = "error_failed_to_open"
73
70
 
74
71
 
75
- # def is_event(action: Action) -> bool:
76
- # return action.value.startswith("event_")
77
-
78
-
79
72
  def is_action_error(action: Action) -> bool:
80
73
  return action.value.startswith("error_")
81
74
 
82
-
83
75
  class BackendException(Exception):
84
76
  pass
85
77
 
86
-
87
78
  class ValidFragment(Protocol):
88
79
  data: bytes
89
80
 
90
-
91
81
  @dataclass
92
82
  class Fragment:
93
83
  data: bytes
@@ -104,23 +94,6 @@ class Fragment:
104
94
  # raise IndexError('Cannot index invalid fragment')
105
95
  return Fragment(self.data[key], self.timestamp)
106
96
 
107
-
108
- # def get_conn_addresses(conn: Connection) -> tuple[str, str]:
109
- # try:
110
- # fd = conn.fileno()
111
- # except OSError:
112
- # return ("closed", "closed")
113
- # else:
114
- # sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
115
- # try:
116
- # # address, port = sock.getpeername() # (IP, port) tuple
117
- # peer_address = sock.getpeername()
118
- # sock_address = sock.getsockname()
119
- # return sock_address, peer_address
120
- # except Exception:
121
- # return ("error", "closed")
122
-
123
-
124
97
  BackendResponse = tuple[object, ...]
125
98
 
126
99
 
@@ -199,10 +172,4 @@ class AdapterBackendStatus(Enum):
199
172
 
200
173
  class ClientStatus(Enum):
201
174
  DISCONNECTED = 0
202
- CONNECTED = 1
203
-
204
-
205
- # class StatusSnapshot(TypedDict):
206
- # type : Literal['snapshot']
207
- # adapters :
208
- # #clients : List[str]
175
+ CONNECTED = 1
@@ -41,7 +41,6 @@ class BackendLogger(threading.Thread):
41
41
  self.conn_description = conn.local()
42
42
  self._logger.info("Backend connected")
43
43
 
44
-
45
44
  try:
46
45
  backend_request(conn.conn, Action.SET_ROLE_LOGGER)
47
46
  except BackendCommunicationError: