syndesi 0.4.2__py3-none-any.whl → 0.4.4__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/__init__.py +16 -1
- syndesi/adapters/adapter.py +147 -72
- syndesi/adapters/auto.py +26 -15
- syndesi/adapters/backend/adapter_backend.py +3 -3
- syndesi/adapters/backend/adapter_session.py +8 -9
- syndesi/adapters/backend/backend.py +7 -2
- syndesi/adapters/backend/ip_backend.py +44 -41
- syndesi/adapters/backend/serialport_backend.py +17 -12
- syndesi/adapters/backend/stop_condition_backend.py +8 -5
- syndesi/adapters/backend/visa_backend.py +3 -3
- syndesi/adapters/ip.py +30 -24
- syndesi/adapters/ip_server.py +9 -3
- syndesi/adapters/serialport.py +20 -7
- syndesi/adapters/stop_condition.py +84 -60
- syndesi/adapters/timeout.py +1 -0
- syndesi/adapters/visa.py +11 -3
- syndesi/cli/console.py +1 -1
- syndesi/cli/shell.py +2 -2
- syndesi/component.py +79 -0
- syndesi/protocols/delimited.py +2 -2
- syndesi/protocols/modbus.py +7 -6
- syndesi/protocols/protocol.py +7 -1
- syndesi/protocols/raw.py +2 -2
- syndesi/protocols/scpi.py +1 -1
- syndesi/scripts/syndesi.py +1 -1
- syndesi/tools/backend_api.py +8 -1
- syndesi/tools/errors.py +24 -4
- syndesi/tools/log.py +1 -0
- syndesi/tools/types.py +1 -0
- syndesi/version.py +5 -1
- {syndesi-0.4.2.dist-info → syndesi-0.4.4.dist-info}/METADATA +1 -1
- syndesi-0.4.4.dist-info/RECORD +61 -0
- syndesi-0.4.2.dist-info/RECORD +0 -60
- {syndesi-0.4.2.dist-info → syndesi-0.4.4.dist-info}/WHEEL +0 -0
- {syndesi-0.4.2.dist-info → syndesi-0.4.4.dist-info}/entry_points.txt +0 -0
- {syndesi-0.4.2.dist-info → syndesi-0.4.4.dist-info}/licenses/LICENSE +0 -0
- {syndesi-0.4.2.dist-info → syndesi-0.4.4.dist-info}/top_level.txt +0 -0
|
@@ -10,7 +10,13 @@ from typing import cast
|
|
|
10
10
|
|
|
11
11
|
import _socket
|
|
12
12
|
|
|
13
|
-
from
|
|
13
|
+
from syndesi.tools.errors import AdapterConfigurationError, AdapterFailedToOpen
|
|
14
|
+
|
|
15
|
+
from ...tools.backend_api import (
|
|
16
|
+
DEFAULT_ADAPTER_OPEN_TIMEOUT,
|
|
17
|
+
AdapterBackendStatus,
|
|
18
|
+
Fragment,
|
|
19
|
+
)
|
|
14
20
|
from .adapter_backend import AdapterBackend, HasFileno
|
|
15
21
|
from .descriptors import IPDescriptor
|
|
16
22
|
|
|
@@ -55,42 +61,37 @@ class IPBackend(AdapterBackend):
|
|
|
55
61
|
def selectable(self) -> HasFileno | None:
|
|
56
62
|
return self._socket
|
|
57
63
|
|
|
58
|
-
def open(self)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
f"Invalid transport protocol : {self.descriptor.transport}"
|
|
77
|
-
)
|
|
78
|
-
try:
|
|
79
|
-
self._socket.settimeout(
|
|
80
|
-
0.5
|
|
81
|
-
) # TODO : Configure this cleanly, it has to be less than the receive timeout of the frontend
|
|
82
|
-
self._socket.connect((self.descriptor.address, self.descriptor.port))
|
|
83
|
-
except OSError as e: # TODO : Maybe change the exception ?
|
|
84
|
-
self._logger.error(f"Failed to open adapter {self.descriptor} : {e}")
|
|
64
|
+
def open(self):
|
|
65
|
+
if self._status == AdapterBackendStatus.CONNECTED:
|
|
66
|
+
self._logger.warning(f"Adapter {self.descriptor} already openend")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
if self.descriptor.port is None:
|
|
70
|
+
raise AdapterConfigurationError("Cannot open adapter without specifying a port")
|
|
71
|
+
|
|
72
|
+
if self._socket is None:
|
|
73
|
+
if self.descriptor.transport == IPDescriptor.Transport.TCP:
|
|
74
|
+
self._socket = cast(
|
|
75
|
+
_socket.socket,
|
|
76
|
+
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
|
|
77
|
+
)
|
|
78
|
+
elif self.descriptor.transport == IPDescriptor.Transport.UDP:
|
|
79
|
+
self._socket = cast(
|
|
80
|
+
_socket.socket, socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
81
|
+
)
|
|
85
82
|
else:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
raise AdapterConfigurationError(
|
|
84
|
+
f"Invalid transport protocol : {self.descriptor.transport}"
|
|
85
|
+
)
|
|
86
|
+
try:
|
|
87
|
+
self._socket.settimeout(DEFAULT_ADAPTER_OPEN_TIMEOUT)
|
|
88
|
+
self._socket.connect((self.descriptor.address, self.descriptor.port))
|
|
89
|
+
except OSError as e:
|
|
90
|
+
self._logger.error(f"Failed to open adapter {self.descriptor} : {e}")
|
|
91
|
+
raise AdapterFailedToOpen(str(e))
|
|
89
92
|
else:
|
|
90
|
-
self.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return output
|
|
93
|
+
self._status = AdapterBackendStatus.CONNECTED
|
|
94
|
+
self._logger.info(f"IP Adapter {self.descriptor} opened")
|
|
94
95
|
|
|
95
96
|
def close(self) -> bool:
|
|
96
97
|
super().close()
|
|
@@ -118,14 +119,16 @@ class IPBackend(AdapterBackend):
|
|
|
118
119
|
self._logger.error(f"Cannot write to closed adapter {self.descriptor}")
|
|
119
120
|
return False
|
|
120
121
|
try:
|
|
121
|
-
self._socket.send(data)
|
|
122
|
-
except (BrokenPipeError, OSError)
|
|
122
|
+
ok = self._socket.send(data) == len(data)
|
|
123
|
+
except (BrokenPipeError, OSError):
|
|
123
124
|
# Socket has been disconnected by the remote peer
|
|
124
|
-
|
|
125
|
+
ok = False
|
|
126
|
+
|
|
127
|
+
if not ok:
|
|
128
|
+
self._logger.error(f"Failed to write to adapter {self.descriptor}")
|
|
125
129
|
self.close()
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return True
|
|
130
|
+
|
|
131
|
+
return ok
|
|
129
132
|
|
|
130
133
|
def _socket_read(self) -> Fragment:
|
|
131
134
|
# This function is called only if the socket was ready
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
import time
|
|
8
8
|
|
|
9
9
|
import serial
|
|
10
|
-
from serial.serialutil import PortNotOpenError
|
|
10
|
+
from serial.serialutil import PortNotOpenError, SerialException
|
|
11
11
|
|
|
12
12
|
from syndesi.tools.backend_api import AdapterBackendStatus, Fragment
|
|
13
|
+
from syndesi.tools.errors import AdapterConfigurationError, AdapterFailedToOpen
|
|
13
14
|
|
|
14
15
|
from .adapter_backend import AdapterBackend, HasFileno
|
|
15
16
|
from .descriptors import SerialPortDescriptor
|
|
@@ -31,8 +32,6 @@ class SerialPortBackend(AdapterBackend):
|
|
|
31
32
|
self._port: serial.Serial | None = None
|
|
32
33
|
self._rts_cts = False
|
|
33
34
|
|
|
34
|
-
self.open()
|
|
35
|
-
|
|
36
35
|
def set_baudrate(self, baudrate: int) -> None:
|
|
37
36
|
"""
|
|
38
37
|
Set baudrate
|
|
@@ -72,27 +71,33 @@ class SerialPortBackend(AdapterBackend):
|
|
|
72
71
|
self.close()
|
|
73
72
|
self.open()
|
|
74
73
|
|
|
75
|
-
def open(self) ->
|
|
74
|
+
def open(self) -> None:
|
|
76
75
|
if self.descriptor.baudrate is None:
|
|
77
|
-
raise
|
|
76
|
+
raise AdapterConfigurationError("Baudrate must be set, please use set_baudrate")
|
|
78
77
|
|
|
79
78
|
if self._port is None:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
try:
|
|
80
|
+
self._port = serial.Serial(
|
|
81
|
+
port=self.descriptor.port,
|
|
82
|
+
baudrate=self.descriptor.baudrate,
|
|
83
|
+
rtscts=self._rts_cts,
|
|
84
|
+
)
|
|
85
|
+
except SerialException as e:
|
|
86
|
+
if 'No such file' in str(e):
|
|
87
|
+
raise AdapterFailedToOpen(f"No such file or directory '{self.descriptor.port}'") from e
|
|
88
|
+
else:
|
|
89
|
+
raise AdapterFailedToOpen('Unknown error') from e
|
|
90
|
+
|
|
85
91
|
elif not self._port.isOpen(): # type: ignore
|
|
86
92
|
self._port.open()
|
|
87
93
|
|
|
88
94
|
if self._port.isOpen(): # type: ignore
|
|
89
95
|
self._logger.info(f"Adapter {self.descriptor} opened")
|
|
90
96
|
self._status = AdapterBackendStatus.CONNECTED
|
|
91
|
-
return True
|
|
92
97
|
else:
|
|
93
98
|
self._logger.error(f"Failed to open adapter {self.descriptor}")
|
|
94
99
|
self._status = AdapterBackendStatus.DISCONNECTED
|
|
95
|
-
|
|
100
|
+
raise AdapterFailedToOpen('Unknown error')
|
|
96
101
|
|
|
97
102
|
def close(self) -> bool:
|
|
98
103
|
super().close()
|
|
@@ -63,9 +63,12 @@ class StopConditionBackend:
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
class TerminationBackend(StopConditionBackend):
|
|
66
|
-
def __init__(self, sequence: bytes) -> None:
|
|
66
|
+
def __init__(self, sequence: bytes | str) -> None:
|
|
67
67
|
super().__init__()
|
|
68
|
-
|
|
68
|
+
if isinstance(sequence, str):
|
|
69
|
+
self._sequence = sequence.encode('utf-8')
|
|
70
|
+
else:
|
|
71
|
+
self._sequence = sequence
|
|
69
72
|
self._sequence_found_length = 0
|
|
70
73
|
|
|
71
74
|
def initiate_read(self) -> None:
|
|
@@ -210,10 +213,10 @@ def stop_condition_to_backend(stop_condition: StopCondition) -> StopConditionBac
|
|
|
210
213
|
if isinstance(stop_condition, Termination):
|
|
211
214
|
return TerminationBackend(stop_condition.sequence)
|
|
212
215
|
elif isinstance(stop_condition, Length):
|
|
213
|
-
return LengthBackend(stop_condition.
|
|
216
|
+
return LengthBackend(stop_condition.n)
|
|
214
217
|
elif isinstance(stop_condition, Continuation):
|
|
215
|
-
return ContinuationBackend(stop_condition.
|
|
218
|
+
return ContinuationBackend(stop_condition.time)
|
|
216
219
|
elif isinstance(stop_condition, Total):
|
|
217
|
-
return TotalBackend(stop_condition.
|
|
220
|
+
return TotalBackend(stop_condition.time)
|
|
218
221
|
else:
|
|
219
222
|
raise RuntimeError(f"Invalid stop condition : {stop_condition}")
|
|
@@ -16,7 +16,7 @@ from typing import TYPE_CHECKING
|
|
|
16
16
|
from ...tools.backend_api import AdapterBackendStatus, Fragment
|
|
17
17
|
from .adapter_backend import (
|
|
18
18
|
AdapterBackend,
|
|
19
|
-
|
|
19
|
+
AdapterDisconnectedSignal,
|
|
20
20
|
AdapterSignal,
|
|
21
21
|
HasFileno,
|
|
22
22
|
)
|
|
@@ -143,7 +143,7 @@ class VisaBackend(AdapterBackend):
|
|
|
143
143
|
self._notify_recv.recv(1)
|
|
144
144
|
if not self._event_queue.empty():
|
|
145
145
|
event = self._event_queue.get()
|
|
146
|
-
if isinstance(event,
|
|
146
|
+
if isinstance(event, AdapterDisconnectedSignal):
|
|
147
147
|
return Fragment(b"", None)
|
|
148
148
|
|
|
149
149
|
with self._fragment_lock:
|
|
@@ -181,7 +181,7 @@ class VisaBackend(AdapterBackend):
|
|
|
181
181
|
# Tell the session that there's data (write to a virtual socket)
|
|
182
182
|
self._notify_send.send(b"1")
|
|
183
183
|
except (TypeError, pyvisa.InvalidSession, BrokenPipeError):
|
|
184
|
-
event_queue.put(
|
|
184
|
+
event_queue.put(AdapterDisconnectedSignal())
|
|
185
185
|
self._notify_send.send(b"1")
|
|
186
186
|
with self._stop_lock:
|
|
187
187
|
if self.stop:
|
syndesi/adapters/ip.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# File : ip.py
|
|
2
2
|
# Author : Sébastien Deriaz
|
|
3
3
|
# License : GPL
|
|
4
|
-
#
|
|
5
|
-
# IP adapter, communicates with TCP or UDP
|
|
6
|
-
|
|
7
4
|
|
|
5
|
+
"""
|
|
6
|
+
IP adapter, communicates with TCP or UDP
|
|
7
|
+
"""
|
|
8
8
|
from collections.abc import Callable
|
|
9
9
|
from types import EllipsisType
|
|
10
10
|
|
|
@@ -18,10 +18,33 @@ from .timeout import Timeout
|
|
|
18
18
|
|
|
19
19
|
# TODO : Server ? create an adapter from a socket ?
|
|
20
20
|
|
|
21
|
-
# TODO : Manage opening and closing, modes ? open at instance or at write/read ?
|
|
21
|
+
# TODO : Manage opening and closing, modes ? open at instance or at write/read ?
|
|
22
|
+
# close after read ? error if already opened before / strict mode ?
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class IP(Adapter):
|
|
26
|
+
"""
|
|
27
|
+
IP Adapter, allows for communication with IP devices through UDP or TCP
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
address : str
|
|
32
|
+
IP description
|
|
33
|
+
port : int
|
|
34
|
+
IP port
|
|
35
|
+
transport : str
|
|
36
|
+
'TCP' or 'UDP'
|
|
37
|
+
timeout : Timeout | float
|
|
38
|
+
Specify communication timeout
|
|
39
|
+
stop_condition : StopCondition
|
|
40
|
+
Specify a read stop condition (None by default)
|
|
41
|
+
auto_open : bool
|
|
42
|
+
Automatically open the adapter
|
|
43
|
+
socket : socket.socket
|
|
44
|
+
Specify a custom socket, this is reserved for server application
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
"""
|
|
25
48
|
DEFAULT_PROTOCOL = IPDescriptor.Transport.TCP
|
|
26
49
|
|
|
27
50
|
def __init__(
|
|
@@ -29,6 +52,7 @@ class IP(Adapter):
|
|
|
29
52
|
address: str,
|
|
30
53
|
port: int | None = None,
|
|
31
54
|
transport: str = DEFAULT_PROTOCOL.value,
|
|
55
|
+
*,
|
|
32
56
|
timeout: Timeout | NumberLike | None | EllipsisType = ...,
|
|
33
57
|
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
34
58
|
alias: str = "",
|
|
@@ -39,25 +63,7 @@ class IP(Adapter):
|
|
|
39
63
|
backend_port: int | None = None,
|
|
40
64
|
):
|
|
41
65
|
"""
|
|
42
|
-
IP adapter
|
|
43
|
-
|
|
44
|
-
Parameters
|
|
45
|
-
----------
|
|
46
|
-
address : str
|
|
47
|
-
IP description
|
|
48
|
-
port : int
|
|
49
|
-
IP port
|
|
50
|
-
transport : str
|
|
51
|
-
'TCP' or 'UDP'
|
|
52
|
-
timeout : Timeout | float
|
|
53
|
-
Specify communication timeout
|
|
54
|
-
stop_condition : StopCondition
|
|
55
|
-
Specify a read stop condition (None by default)
|
|
56
|
-
auto_open : bool
|
|
57
|
-
Automatically open the adapter
|
|
58
|
-
socket : socket.socket
|
|
59
|
-
Specify a custom socket, this is reserved for server application
|
|
60
|
-
|
|
66
|
+
Instanciate new IP adapter
|
|
61
67
|
"""
|
|
62
68
|
super().__init__(
|
|
63
69
|
descriptor=IPDescriptor(
|
|
@@ -107,4 +113,4 @@ class IP(Adapter):
|
|
|
107
113
|
transport : str | IPDescriptor.Transport
|
|
108
114
|
"""
|
|
109
115
|
if self.descriptor.transport is None:
|
|
110
|
-
self.descriptor.transport = IPDescriptor.Transport(transport)
|
|
116
|
+
self.descriptor.transport = IPDescriptor.Transport(transport)
|
syndesi/adapters/ip_server.py
CHANGED
|
@@ -91,12 +91,18 @@
|
|
|
91
91
|
# self._logger.info("Adapter closed !")
|
|
92
92
|
# self._opened = False
|
|
93
93
|
|
|
94
|
-
# def get_client(self,
|
|
94
|
+
# def get_client(self,
|
|
95
|
+
# stop_condition : StopConditionBackend = None,
|
|
96
|
+
# timeout : Timeout = None) -> IP:
|
|
95
97
|
# """
|
|
96
98
|
# Wait for a client to connect to the server and return the corresponding adapter
|
|
97
99
|
# """
|
|
98
100
|
# if not self._opened:
|
|
99
101
|
# raise RuntimeError("open() must be called before getting client")
|
|
100
102
|
# client_socket, address = self._socket.accept()
|
|
101
|
-
# default_timeout = Timeout(response=None,
|
|
102
|
-
#
|
|
103
|
+
# default_timeout = Timeout(response=None,
|
|
104
|
+
# continuation=IP.DEFAULT_CONTINUATION_TIMEOUT,
|
|
105
|
+
# total=None)
|
|
106
|
+
# return IP(_socket=client_socket, address=address,
|
|
107
|
+
# stop_condition=stop_condition,
|
|
108
|
+
# timeout=timeout_fuse(timeout, default_timeout))
|
syndesi/adapters/serialport.py
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
# Author : Sébastien Deriaz
|
|
3
3
|
# License : GPL
|
|
4
4
|
|
|
5
|
+
"""
|
|
6
|
+
SerialPort module, allows communication with serial devices using
|
|
7
|
+
the OS layers (COMx, /dev/ttyUSBx or /dev/ttyACMx)
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
5
11
|
from collections.abc import Callable
|
|
6
12
|
from types import EllipsisType
|
|
7
13
|
|
|
@@ -15,10 +21,21 @@ from .timeout import Timeout
|
|
|
15
21
|
|
|
16
22
|
|
|
17
23
|
class SerialPort(Adapter):
|
|
24
|
+
"""
|
|
25
|
+
Serial communication adapter
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
port : str
|
|
30
|
+
Serial port (COMx or ttyACMx)
|
|
31
|
+
baudrate : int
|
|
32
|
+
Baudrate
|
|
33
|
+
"""
|
|
18
34
|
def __init__(
|
|
19
35
|
self,
|
|
20
36
|
port: str,
|
|
21
37
|
baudrate: int | None = None,
|
|
38
|
+
*,
|
|
22
39
|
timeout: Timeout | NumberLike | None | EllipsisType = ...,
|
|
23
40
|
stop_conditions: StopCondition | list[StopCondition] | EllipsisType = ...,
|
|
24
41
|
alias: str = "",
|
|
@@ -28,12 +45,7 @@ class SerialPort(Adapter):
|
|
|
28
45
|
backend_port: int | None = None,
|
|
29
46
|
) -> None:
|
|
30
47
|
"""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Parameters
|
|
34
|
-
----------
|
|
35
|
-
port : str
|
|
36
|
-
Serial port (COMx or ttyACMx)
|
|
48
|
+
Instanciate new SerialPort adapter
|
|
37
49
|
"""
|
|
38
50
|
descriptor = SerialPortDescriptor(port, baudrate)
|
|
39
51
|
super().__init__(
|
|
@@ -48,7 +60,8 @@ class SerialPort(Adapter):
|
|
|
48
60
|
self.descriptor: SerialPortDescriptor
|
|
49
61
|
|
|
50
62
|
self._logger.info(
|
|
51
|
-
f"Setting up SerialPort adapter {self.descriptor},
|
|
63
|
+
f"Setting up SerialPort adapter {self.descriptor}, \
|
|
64
|
+
timeout={timeout} and stop_conditions={self._stop_conditions}"
|
|
52
65
|
)
|
|
53
66
|
|
|
54
67
|
self.open()
|
|
@@ -2,89 +2,113 @@
|
|
|
2
2
|
# Author : Sébastien Deriaz
|
|
3
3
|
# License : GPL
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
"""
|
|
6
|
+
Stop-condition module
|
|
7
|
+
|
|
8
|
+
This is the frontend of the stop-conditions, the part that is imported by the user
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
#from abc import abstractmethod
|
|
6
12
|
from enum import Enum
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
7
15
|
|
|
8
16
|
class StopConditionType(Enum):
|
|
17
|
+
"""
|
|
18
|
+
Stop-condition type
|
|
19
|
+
"""
|
|
9
20
|
TERMINATION = "termination"
|
|
10
21
|
LENGTH = "length"
|
|
11
22
|
TIMEOUT = "timeout"
|
|
12
23
|
|
|
24
|
+
@dataclass
|
|
13
25
|
class StopCondition:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def
|
|
19
|
-
|
|
20
|
-
A condition to stop reading from a device
|
|
21
|
-
|
|
22
|
-
Cannot be used on its own
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class Termination(StopCondition):
|
|
27
|
-
def __init__(self, sequence: bytes | str) -> None:
|
|
28
|
-
"""
|
|
29
|
-
Stop reading once the desired sequence is detected
|
|
30
|
-
|
|
31
|
-
Parameters
|
|
32
|
-
----------
|
|
33
|
-
sequence : bytes
|
|
34
|
-
"""
|
|
35
|
-
self.sequence: bytes
|
|
36
|
-
if isinstance(sequence, str):
|
|
37
|
-
self.sequence = sequence.encode("utf-8")
|
|
38
|
-
elif isinstance(sequence, bytes):
|
|
39
|
-
self.sequence = sequence
|
|
40
|
-
else:
|
|
41
|
-
raise ValueError(f"Invalid termination sequence type : {type(sequence)}")
|
|
26
|
+
"""
|
|
27
|
+
Stop-condition base class, cannot be used on its own
|
|
28
|
+
"""
|
|
29
|
+
# @abstractmethod
|
|
30
|
+
# def type(self) -> StopConditionType:
|
|
31
|
+
# pass
|
|
42
32
|
|
|
43
33
|
def __repr__(self) -> str:
|
|
44
34
|
return self.__str__()
|
|
45
35
|
|
|
36
|
+
@dataclass
|
|
37
|
+
class Termination(StopCondition):
|
|
38
|
+
"""
|
|
39
|
+
Termination stop-condition, used to stop when a specified sequence is received
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
sequence : bytes | str
|
|
44
|
+
"""
|
|
45
|
+
#TYPE = StopConditionType.TERMINATION
|
|
46
|
+
sequence : bytes | str
|
|
47
|
+
# def __init__(self, sequence: bytes | str) -> None:
|
|
48
|
+
# """
|
|
49
|
+
# Instanciate a new Termination class
|
|
50
|
+
# """
|
|
51
|
+
# self.sequence: bytes
|
|
52
|
+
# if isinstance(sequence, str):
|
|
53
|
+
# self.sequence = sequence.encode("utf-8")
|
|
54
|
+
# elif isinstance(sequence, bytes):
|
|
55
|
+
# self.sequence = sequence
|
|
56
|
+
# else:
|
|
57
|
+
# raise ValueError(f"Invalid termination sequence type : {type(sequence)}")
|
|
58
|
+
|
|
46
59
|
def __str__(self) -> str:
|
|
47
60
|
return f"Termination({repr(self.sequence)})"
|
|
48
61
|
|
|
49
|
-
def type(self) -> StopConditionType:
|
|
50
|
-
return StopConditionType.TERMINATION
|
|
51
|
-
|
|
52
62
|
|
|
63
|
+
@dataclass
|
|
53
64
|
class Length(StopCondition):
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def __repr__(self) -> str:
|
|
66
|
-
return self.__str__()
|
|
65
|
+
"""
|
|
66
|
+
Length stop-condition, used to stop when the specified number of bytes (or more) have been read
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
N : int
|
|
71
|
+
Number of bytes
|
|
72
|
+
"""
|
|
73
|
+
#TYPE = StopConditionType.LENGTH
|
|
74
|
+
n : int
|
|
67
75
|
|
|
68
76
|
def __str__(self) -> str:
|
|
69
|
-
return f"Length({self.
|
|
70
|
-
|
|
71
|
-
def type(self) -> StopConditionType:
|
|
72
|
-
return StopConditionType.LENGTH
|
|
77
|
+
return f"Length({self.n})"
|
|
73
78
|
|
|
79
|
+
# def type(self) -> StopConditionType:
|
|
80
|
+
# return StopConditionType.LENGTH
|
|
74
81
|
|
|
82
|
+
@dataclass
|
|
75
83
|
class Continuation(StopCondition):
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
"""
|
|
85
|
+
Continuation stop-condition, used to stop reading when data has already been received
|
|
86
|
+
and nothing has been received since then for the specified amount of time
|
|
79
87
|
|
|
80
|
-
|
|
81
|
-
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
time : float
|
|
91
|
+
"""
|
|
92
|
+
#TYPE = StopConditionType.TIMEOUT
|
|
93
|
+
time : float
|
|
82
94
|
|
|
83
95
|
|
|
84
|
-
class Total(StopCondition):
|
|
85
|
-
def __init__(self, time: float) -> None:
|
|
86
|
-
super().__init__()
|
|
87
|
-
self.total = time
|
|
88
96
|
|
|
89
|
-
def type(self) -> StopConditionType:
|
|
90
|
-
|
|
97
|
+
# def type(self) -> StopConditionType:
|
|
98
|
+
# return StopConditionType.TIMEOUT
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class Total(StopCondition):
|
|
102
|
+
"""
|
|
103
|
+
Total stop-condition, used to stop reading when data has already been received
|
|
104
|
+
and the total read time exceeds the specified amount
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
#TYPE = StopConditionType.TIMEOUT
|
|
108
|
+
time : float
|
|
109
|
+
# def __init__(self, time: float) -> None:
|
|
110
|
+
# super().__init__()
|
|
111
|
+
# self.total = time
|
|
112
|
+
|
|
113
|
+
# def type(self) -> StopConditionType:
|
|
114
|
+
# return StopConditionType.TIMEOUT
|
syndesi/adapters/timeout.py
CHANGED
syndesi/adapters/visa.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# File : visa.py
|
|
2
2
|
# Author : Sébastien Deriaz
|
|
3
3
|
# License : GPL
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
VISA adatper, uses a VISA backend like pyvisa-py or NI to communicate with instruments
|
|
7
|
+
"""
|
|
6
8
|
|
|
7
9
|
from collections.abc import Callable
|
|
8
10
|
from types import EllipsisType
|
|
@@ -16,9 +18,15 @@ from .timeout import Timeout
|
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
class Visa(Adapter):
|
|
21
|
+
"""
|
|
22
|
+
VISA Adapter, allows for communication with VISA-compatible devices.
|
|
23
|
+
It uses pyvisa under the hood
|
|
24
|
+
|
|
25
|
+
"""
|
|
19
26
|
def __init__(
|
|
20
27
|
self,
|
|
21
28
|
descriptor: str,
|
|
29
|
+
*,
|
|
22
30
|
alias: str = "",
|
|
23
31
|
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
24
32
|
timeout: None | float | Timeout | EllipsisType = ...,
|
|
@@ -28,7 +36,7 @@ class Visa(Adapter):
|
|
|
28
36
|
backend_port: int | None = None,
|
|
29
37
|
) -> None:
|
|
30
38
|
super().__init__(
|
|
31
|
-
VisaDescriptor.from_string(descriptor),
|
|
39
|
+
descriptor=VisaDescriptor.from_string(descriptor),
|
|
32
40
|
alias=alias,
|
|
33
41
|
stop_conditions=stop_conditions,
|
|
34
42
|
timeout=timeout,
|
syndesi/cli/console.py
CHANGED
syndesi/cli/shell.py
CHANGED
|
@@ -8,7 +8,7 @@ from enum import Enum
|
|
|
8
8
|
|
|
9
9
|
from syndesi.adapters.adapter import Adapter
|
|
10
10
|
from syndesi.adapters.backend.adapter_backend import (
|
|
11
|
-
|
|
11
|
+
AdapterDisconnectedSignal,
|
|
12
12
|
AdapterReadPayload,
|
|
13
13
|
AdapterSignal,
|
|
14
14
|
)
|
|
@@ -214,7 +214,7 @@ class AdapterShell:
|
|
|
214
214
|
self._protocol.write(command)
|
|
215
215
|
|
|
216
216
|
def event(self, signal: AdapterSignal) -> None:
|
|
217
|
-
if isinstance(signal,
|
|
217
|
+
if isinstance(signal, AdapterDisconnectedSignal):
|
|
218
218
|
|
|
219
219
|
def f(answer: str) -> None:
|
|
220
220
|
if answer.lower() == "y":
|