syndesi 0.2.3__py3-none-any.whl → 0.2.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 +3 -5
- syndesi/_version.py +1 -0
- syndesi/adapters/adapter.py +50 -43
- syndesi/adapters/auto.py +1 -1
- syndesi/adapters/ip.py +31 -31
- syndesi/adapters/serialport.py +84 -63
- syndesi/adapters/stop_conditions.py +39 -2
- syndesi/adapters/timeout.py +58 -51
- syndesi/adapters/visa.py +54 -8
- syndesi/cli/adapter.py +134 -0
- syndesi/cli/command.py +29 -0
- syndesi/cli/ip.py +0 -0
- syndesi/cli/modbus.py +0 -0
- syndesi/cli/serial.py +18 -0
- syndesi/cli/shell.py +26 -10
- syndesi/cli/syndesi.py +30 -15
- syndesi/protocols/__init__.py +2 -1
- syndesi/protocols/delimited.py +11 -6
- syndesi/protocols/modbus.py +221 -63
- syndesi/protocols/protocol.py +7 -3
- syndesi/protocols/raw.py +1 -2
- syndesi/protocols/scpi.py +7 -7
- syndesi/tools/log.py +30 -62
- syndesi/tools/others.py +2 -1
- syndesi/version.py +1 -0
- {syndesi-0.2.3.dist-info → syndesi-0.2.4.dist-info}/METADATA +2 -3
- {syndesi-0.2.3.dist-info → syndesi-0.2.4.dist-info}/RECORD +30 -24
- syndesi-0.2.3.dist-info/LICENSE +0 -674
- {syndesi-0.2.3.dist-info → syndesi-0.2.4.dist-info}/WHEEL +0 -0
- {syndesi-0.2.3.dist-info → syndesi-0.2.4.dist-info}/entry_points.txt +0 -0
- {syndesi-0.2.3.dist-info → syndesi-0.2.4.dist-info}/top_level.txt +0 -0
syndesi/__init__.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
from .tools.log import
|
|
1
|
+
from .tools.log import log_settings
|
|
2
2
|
|
|
3
|
-
from .adapters
|
|
4
|
-
from .
|
|
5
|
-
from .protocols.delimited import Delimited
|
|
6
|
-
from .protocols.scpi import SCPI
|
|
3
|
+
from .adapters import IP, SerialPort
|
|
4
|
+
from .protocols import Modbus, Delimited, Raw, SCPI
|
syndesi/_version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.2.3'
|
syndesi/adapters/adapter.py
CHANGED
|
@@ -29,9 +29,7 @@ import socket
|
|
|
29
29
|
import logging
|
|
30
30
|
from time import time
|
|
31
31
|
from dataclasses import dataclass
|
|
32
|
-
from ..tools.others import DEFAULT
|
|
33
32
|
|
|
34
|
-
DEFAULT_TIMEOUT = Timeout(response=5, continuation=200e-3, total=None)
|
|
35
33
|
DEFAULT_STOP_CONDITION = None
|
|
36
34
|
|
|
37
35
|
|
|
@@ -72,7 +70,7 @@ class Adapter(ABC):
|
|
|
72
70
|
DISCONNECTED = 0
|
|
73
71
|
CONNECTED = 1
|
|
74
72
|
|
|
75
|
-
def __init__(self, alias : str = '', stop_condition : Union[StopCondition, None] =
|
|
73
|
+
def __init__(self, alias : str = '', stop_condition : Union[StopCondition, None] = ..., timeout : Union[float, Timeout] = ...) -> None:
|
|
76
74
|
"""
|
|
77
75
|
Adapter instance
|
|
78
76
|
|
|
@@ -86,13 +84,21 @@ class Adapter(ABC):
|
|
|
86
84
|
Default to None
|
|
87
85
|
"""
|
|
88
86
|
super().__init__()
|
|
87
|
+
|
|
89
88
|
self._alias = alias
|
|
90
89
|
|
|
91
|
-
self.
|
|
90
|
+
self.is_default_timeout = timeout is Ellipsis
|
|
91
|
+
if self.is_default_timeout:
|
|
92
|
+
self._timeout = self._default_timeout()
|
|
93
|
+
else:
|
|
94
|
+
self._timeout = timeout_fuse(timeout, self._default_timeout())
|
|
95
|
+
|
|
96
|
+
self._default_stop_condition = stop_condition is Ellipsis
|
|
92
97
|
if self._default_stop_condition:
|
|
93
98
|
self._stop_condition = DEFAULT_STOP_CONDITION
|
|
94
99
|
else:
|
|
95
100
|
self._stop_condition = stop_condition
|
|
101
|
+
|
|
96
102
|
self._read_queue = TimedQueue()
|
|
97
103
|
self._thread : Union[Thread, None] = None
|
|
98
104
|
self._status = self.Status.DISCONNECTED
|
|
@@ -103,16 +109,12 @@ class Adapter(ABC):
|
|
|
103
109
|
# not used because of termination or length stop condition
|
|
104
110
|
self._previous_buffer = b''
|
|
105
111
|
|
|
106
|
-
self.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
elif isinstance(timeout, Timeout):
|
|
113
|
-
self._timeout = timeout
|
|
114
|
-
else:
|
|
115
|
-
raise ValueError(f"Invalid timeout type : {type(timeout)}")
|
|
112
|
+
if not isinstance(self._timeout, Timeout):
|
|
113
|
+
raise ValueError('Timeout must be defined to initialize an Adapter base class')
|
|
114
|
+
|
|
115
|
+
@abstractmethod
|
|
116
|
+
def _default_timeout(self):
|
|
117
|
+
pass
|
|
116
118
|
|
|
117
119
|
def set_timeout(self, timeout : Timeout):
|
|
118
120
|
"""
|
|
@@ -132,7 +134,7 @@ class Adapter(ABC):
|
|
|
132
134
|
----------
|
|
133
135
|
default_timeout : Timeout or tuple or float
|
|
134
136
|
"""
|
|
135
|
-
if self.
|
|
137
|
+
if self.is_default_timeout:
|
|
136
138
|
self._logger.debug(f'Setting default timeout to {default_timeout}')
|
|
137
139
|
self._timeout = default_timeout
|
|
138
140
|
else:
|
|
@@ -203,8 +205,32 @@ class Adapter(ABC):
|
|
|
203
205
|
"""
|
|
204
206
|
pass
|
|
205
207
|
|
|
208
|
+
@abstractmethod
|
|
209
|
+
def read(self, timeout : Timeout = ..., stop_condition : StopCondition = ..., return_metrics : bool = False) -> bytes:
|
|
210
|
+
pass
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@abstractmethod
|
|
214
|
+
def _start_thread(self):
|
|
215
|
+
self._logger.debug("Starting read thread...")
|
|
216
|
+
|
|
217
|
+
def __del__(self):
|
|
218
|
+
self.close()
|
|
219
|
+
|
|
220
|
+
def query(self, data : Union[bytes, str], timeout : Timeout = ..., stop_condition : StopCondition = ..., return_metrics : bool = False) -> bytes:
|
|
221
|
+
"""
|
|
222
|
+
Shortcut function that combines
|
|
223
|
+
- flush_read
|
|
224
|
+
- write
|
|
225
|
+
- read
|
|
226
|
+
"""
|
|
227
|
+
self.flushRead()
|
|
228
|
+
self.write(data)
|
|
229
|
+
return self.read(timeout=timeout, stop_condition=stop_condition, return_metrics=return_metrics)
|
|
230
|
+
|
|
206
231
|
|
|
207
|
-
|
|
232
|
+
class StreamAdapter(Adapter):
|
|
233
|
+
def read(self, timeout=..., stop_condition=..., return_metrics : bool = False) -> bytes:
|
|
208
234
|
"""
|
|
209
235
|
Read data from the device
|
|
210
236
|
|
|
@@ -220,22 +246,21 @@ class Adapter(ABC):
|
|
|
220
246
|
if self._status == self.Status.DISCONNECTED:
|
|
221
247
|
self.open()
|
|
222
248
|
|
|
223
|
-
#
|
|
224
|
-
if timeout
|
|
249
|
+
# 29.08.24 Change timeout behavior
|
|
250
|
+
if timeout is ...:
|
|
251
|
+
# Use the class timeout
|
|
225
252
|
timeout = self._timeout
|
|
226
|
-
|
|
227
|
-
|
|
253
|
+
else:
|
|
254
|
+
# Fuse it
|
|
255
|
+
timeout = timeout_fuse(timeout, self._timeout)
|
|
228
256
|
|
|
229
|
-
if stop_condition
|
|
257
|
+
if stop_condition is ...:
|
|
230
258
|
stop_condition = self._stop_condition
|
|
231
259
|
|
|
232
260
|
# If the adapter is closed, open it
|
|
233
261
|
if self._status == self.Status.DISCONNECTED:
|
|
234
262
|
self.open()
|
|
235
263
|
|
|
236
|
-
if self._thread is None or not self._thread.is_alive():
|
|
237
|
-
self._start_thread()
|
|
238
|
-
|
|
239
264
|
timeout_ms = timeout.initiate_read(len(self._previous_buffer) > 0)
|
|
240
265
|
|
|
241
266
|
if stop_condition is not None:
|
|
@@ -337,22 +362,4 @@ class Adapter(ABC):
|
|
|
337
362
|
total_time=timeout.total_time
|
|
338
363
|
)
|
|
339
364
|
else:
|
|
340
|
-
return output
|
|
341
|
-
|
|
342
|
-
@abstractmethod
|
|
343
|
-
def _start_thread(self):
|
|
344
|
-
pass
|
|
345
|
-
|
|
346
|
-
def __del__(self):
|
|
347
|
-
self.close()
|
|
348
|
-
|
|
349
|
-
@abstractmethod
|
|
350
|
-
def query(self, data : Union[bytes, str], timeout=None, stop_condition=None, return_metrics : bool = False) -> bytes:
|
|
351
|
-
"""
|
|
352
|
-
Shortcut function that combines
|
|
353
|
-
- flush_read
|
|
354
|
-
- write
|
|
355
|
-
- read
|
|
356
|
-
"""
|
|
357
|
-
pass
|
|
358
|
-
|
|
365
|
+
return output
|
syndesi/adapters/auto.py
CHANGED
|
@@ -17,7 +17,7 @@ from typing import Union
|
|
|
17
17
|
import re
|
|
18
18
|
from . import Adapter, IP, SerialPort
|
|
19
19
|
|
|
20
|
-
IP_PATTERN = '([0-9]
|
|
20
|
+
IP_PATTERN = '([0-9]+.[0-9]+.[0-9]+.[0-9]+)(:[0-9]+)*'
|
|
21
21
|
|
|
22
22
|
WINDOWS_SERIAL_PATTERN = '(COM[0-9]+)(:[0-9]+)*'
|
|
23
23
|
LINUX_SERIAL_PATTERN = '(/dev/tty[a-zA-Z0-9]+)(:[0-9]+)*'
|
syndesi/adapters/ip.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import socket
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from .adapter import
|
|
3
|
+
from .adapter import StreamAdapter, AdapterDisconnected
|
|
4
4
|
from ..tools.types import to_bytes
|
|
5
5
|
from .timeout import Timeout, timeout_fuse
|
|
6
6
|
from .stop_conditions import StopCondition
|
|
@@ -10,18 +10,10 @@ from typing import Union
|
|
|
10
10
|
from time import time
|
|
11
11
|
import argparse
|
|
12
12
|
#from ..cli import shell
|
|
13
|
-
from ..tools.others import DEFAULT
|
|
14
13
|
import select
|
|
15
14
|
|
|
16
|
-
class IP(
|
|
17
|
-
|
|
18
|
-
response=2,
|
|
19
|
-
on_response='error',
|
|
20
|
-
continuation=100e-3,
|
|
21
|
-
on_continuation='return',
|
|
22
|
-
total=5,
|
|
23
|
-
on_total='error')
|
|
24
|
-
DEFAULT_BUFFER_SIZE = 1024
|
|
15
|
+
class IP(StreamAdapter):
|
|
16
|
+
_DEFAULT_BUFFER_SIZE = 1024
|
|
25
17
|
class Protocol(Enum):
|
|
26
18
|
TCP = 'TCP'
|
|
27
19
|
UDP = 'UDP'
|
|
@@ -30,10 +22,10 @@ class IP(Adapter):
|
|
|
30
22
|
address : str,
|
|
31
23
|
port : int = None,
|
|
32
24
|
transport : str = 'TCP',
|
|
33
|
-
timeout : Union[Timeout, float] =
|
|
34
|
-
stop_condition : StopCondition =
|
|
25
|
+
timeout : Union[Timeout, float] = ...,
|
|
26
|
+
stop_condition : StopCondition = ...,
|
|
35
27
|
alias : str = '',
|
|
36
|
-
buffer_size : int =
|
|
28
|
+
buffer_size : int = _DEFAULT_BUFFER_SIZE,
|
|
37
29
|
_socket : socket.socket = None):
|
|
38
30
|
"""
|
|
39
31
|
IP stack adapter
|
|
@@ -56,13 +48,9 @@ class IP(Adapter):
|
|
|
56
48
|
Socket buffer size, may be removed in the future
|
|
57
49
|
socket : socket.socket
|
|
58
50
|
Specify a custom socket, this is reserved for server application
|
|
59
|
-
"""
|
|
60
|
-
if timeout == DEFAULT:
|
|
61
|
-
timeout = self.DEFAULT_TIMEOUT
|
|
62
|
-
else:
|
|
63
|
-
timeout = timeout_fuse(timeout, self.DEFAULT_TIMEOUT)
|
|
64
|
-
|
|
51
|
+
"""
|
|
65
52
|
super().__init__(alias=alias, timeout=timeout, stop_condition=stop_condition)
|
|
53
|
+
|
|
66
54
|
self._transport = self.Protocol(transport)
|
|
67
55
|
self._is_server = _socket is not None
|
|
68
56
|
|
|
@@ -81,6 +69,21 @@ class IP(Adapter):
|
|
|
81
69
|
self._port = port
|
|
82
70
|
self._buffer_size = buffer_size
|
|
83
71
|
|
|
72
|
+
def __str__(self) -> str:
|
|
73
|
+
return f'IP({self._address}:{self._port})'
|
|
74
|
+
|
|
75
|
+
def __repr__(self) -> str:
|
|
76
|
+
return self.__str__()
|
|
77
|
+
|
|
78
|
+
def _default_timeout(self):
|
|
79
|
+
return Timeout(
|
|
80
|
+
response=5,
|
|
81
|
+
on_response='error',
|
|
82
|
+
continuation=100e-3,
|
|
83
|
+
on_continuation='return',
|
|
84
|
+
total=5,
|
|
85
|
+
on_total='error')
|
|
86
|
+
|
|
84
87
|
def set_default_port(self, port):
|
|
85
88
|
"""
|
|
86
89
|
Sets IP port if no port has been set yet.
|
|
@@ -101,9 +104,12 @@ class IP(Adapter):
|
|
|
101
104
|
if self._port is None:
|
|
102
105
|
raise ValueError(f"Cannot open adapter without specifying a port")
|
|
103
106
|
|
|
104
|
-
self._logger.debug(f"Adapter {self._alias}
|
|
107
|
+
self._logger.debug(f"Adapter {self._alias + ' ' if self._alias != '' else ''}connect to ({self._address}, {self._port})")
|
|
105
108
|
self._socket.connect((self._address, self._port))
|
|
106
109
|
self._status = self.Status.CONNECTED
|
|
110
|
+
if self._thread is None or not self._thread.is_alive():
|
|
111
|
+
self._start_thread()
|
|
112
|
+
|
|
107
113
|
self._logger.info(f"Adapter {self._alias} opened !")
|
|
108
114
|
|
|
109
115
|
def close(self):
|
|
@@ -122,7 +128,7 @@ class IP(Adapter):
|
|
|
122
128
|
def write(self, data : Union[bytes, str]):
|
|
123
129
|
data = to_bytes(data)
|
|
124
130
|
if self._status == self.Status.DISCONNECTED:
|
|
125
|
-
self._logger.info(f"Adapter {self._alias}
|
|
131
|
+
self._logger.info(f"Adapter {self._alias + ' ' if self._alias != '' else ''}is closed, opening...")
|
|
126
132
|
self.open()
|
|
127
133
|
write_start = time()
|
|
128
134
|
self._socket.send(data)
|
|
@@ -130,18 +136,14 @@ class IP(Adapter):
|
|
|
130
136
|
self._logger.debug(f"Write [{write_duration*1e3:.3f}ms]: {repr(data)}")
|
|
131
137
|
|
|
132
138
|
def _start_thread(self):
|
|
133
|
-
|
|
139
|
+
super()._start_thread()
|
|
134
140
|
if self._thread is None or not self._thread.is_alive():
|
|
135
141
|
self._thread = Thread(target=self._read_thread, daemon=True, args=(self._socket, self._read_queue, self._thread_stop_read))
|
|
136
142
|
self._thread.start()
|
|
137
143
|
|
|
138
|
-
# # EXPERIMENTAL
|
|
139
|
-
# def read_thread_alive(self):
|
|
140
|
-
# return self._thread.is_alive()
|
|
141
|
-
|
|
142
144
|
def _read_thread(self, socket : socket.socket, read_queue : TimedQueue, stop : socket.socket):
|
|
143
145
|
# Using select.select works on both Windows and Linux as long as the inputs are all sockets
|
|
144
|
-
while True:
|
|
146
|
+
while True:
|
|
145
147
|
|
|
146
148
|
try:
|
|
147
149
|
ready, _, _ = select.select([socket, stop], [], [])
|
|
@@ -172,8 +174,6 @@ class IP(Adapter):
|
|
|
172
174
|
def query(self, data : Union[bytes, str], timeout=None, stop_condition=None, return_metrics : bool = False):
|
|
173
175
|
if self._is_server:
|
|
174
176
|
raise SystemError("Cannot query on server adapters")
|
|
175
|
-
|
|
176
|
-
self.write(data)
|
|
177
|
-
return self.read(timeout=timeout, stop_condition=stop_condition, return_metrics=return_metrics)
|
|
177
|
+
return super().query(data=data, timeout=timeout, stop_condition=stop_condition, return_metrics=return_metrics)
|
|
178
178
|
|
|
179
179
|
|
syndesi/adapters/serialport.py
CHANGED
|
@@ -8,31 +8,19 @@ import socket
|
|
|
8
8
|
import sys
|
|
9
9
|
#from collections.abc import Sequence
|
|
10
10
|
|
|
11
|
-
from .adapter import Adapter, AdapterDisconnected
|
|
11
|
+
from .adapter import Adapter, StreamAdapter, AdapterDisconnected
|
|
12
12
|
from ..tools.types import to_bytes
|
|
13
13
|
from .stop_conditions import *
|
|
14
|
-
from .timeout import Timeout
|
|
14
|
+
from .timeout import Timeout, timeout_fuse
|
|
15
15
|
from .timed_queue import TimedQueue
|
|
16
|
-
#from ..cli import shell
|
|
17
|
-
from ..tools.others import DEFAULT
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
response=1,
|
|
21
|
-
on_response='error',
|
|
22
|
-
continuation=200e-3,
|
|
23
|
-
on_continuation='return',
|
|
24
|
-
total=None,
|
|
25
|
-
on_total='error')
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class SerialPort(Adapter):
|
|
17
|
+
class SerialPort(StreamAdapter):
|
|
31
18
|
def __init__(self,
|
|
32
19
|
port : str,
|
|
33
|
-
baudrate : int
|
|
34
|
-
timeout : Union[Timeout, float] =
|
|
35
|
-
stop_condition : StopCondition =
|
|
20
|
+
baudrate : int = ...,
|
|
21
|
+
timeout : Union[Timeout, float] = ...,
|
|
22
|
+
stop_condition : StopCondition = ...,
|
|
23
|
+
alias : str = '',
|
|
36
24
|
rts_cts : bool = False): # rts_cts experimental
|
|
37
25
|
"""
|
|
38
26
|
Serial communication adapter
|
|
@@ -41,32 +29,87 @@ class SerialPort(Adapter):
|
|
|
41
29
|
----------
|
|
42
30
|
port : str
|
|
43
31
|
Serial port (COMx or ttyACMx)
|
|
44
|
-
"""
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
super().__init__(timeout=timeout, stop_condition=stop_condition)
|
|
49
|
-
self._logger.info(f"Setting up SerialPort adapter timeout:{timeout}, stop_condition:{stop_condition}")
|
|
32
|
+
"""
|
|
33
|
+
super().__init__(timeout=timeout, stop_condition=stop_condition, alias=alias)
|
|
34
|
+
|
|
50
35
|
self._port_name = port
|
|
36
|
+
self._baudrate_set = baudrate is not ...
|
|
51
37
|
self._baudrate = baudrate
|
|
52
|
-
self.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
38
|
+
self._logger.info(f"Setting up SerialPort adapter on {self._port_name} with baudrate={baudrate}, timeout={timeout} and stop_condition={stop_condition}")
|
|
39
|
+
self._port = None
|
|
40
|
+
|
|
41
|
+
self.open()
|
|
42
|
+
|
|
57
43
|
|
|
58
44
|
self._rts_cts = rts_cts
|
|
59
45
|
|
|
46
|
+
def _default_timeout(self):
|
|
47
|
+
return Timeout(
|
|
48
|
+
response=2,
|
|
49
|
+
on_response='error',
|
|
50
|
+
continuation=100e-3,
|
|
51
|
+
on_continuation='return',
|
|
52
|
+
total=None,
|
|
53
|
+
on_total='error')
|
|
54
|
+
|
|
55
|
+
def __str__(self) -> str:
|
|
56
|
+
return f'Serial({self._port_name}:{self._baudrate})'
|
|
57
|
+
|
|
58
|
+
def __repr__(self) -> str:
|
|
59
|
+
return self.__str__()
|
|
60
|
+
|
|
61
|
+
def set_baudrate(self, baudrate):
|
|
62
|
+
"""
|
|
63
|
+
Set baudrate
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
baudrate : int
|
|
68
|
+
"""
|
|
69
|
+
is_connected = self._status == self.Status.CONNECTED
|
|
70
|
+
|
|
71
|
+
if is_connected:
|
|
72
|
+
self.close()
|
|
73
|
+
|
|
74
|
+
self._baudrate = baudrate
|
|
75
|
+
|
|
76
|
+
if is_connected:
|
|
77
|
+
self.open()
|
|
78
|
+
|
|
79
|
+
def set_default_baudrate(self, baudrate):
|
|
80
|
+
"""
|
|
81
|
+
Sets the default baudrate
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
baudrate : int
|
|
86
|
+
"""
|
|
87
|
+
if not self._baudrate_set:
|
|
88
|
+
self._baudrate = baudrate
|
|
89
|
+
|
|
60
90
|
def flushRead(self):
|
|
61
91
|
self._port.flush()
|
|
92
|
+
super().flushRead()
|
|
62
93
|
|
|
63
94
|
def open(self):
|
|
64
|
-
self.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
95
|
+
if self._baudrate is ...:
|
|
96
|
+
raise ValueError('Baudrate must be set, please use set_baudrate')
|
|
97
|
+
|
|
98
|
+
if self._port is None:
|
|
99
|
+
self._port = serial.Serial(port=self._port_name, baudrate=self._baudrate)
|
|
100
|
+
elif not self._port.isOpen():
|
|
101
|
+
self._port.open()
|
|
102
|
+
# # Flush the input buffer
|
|
103
|
+
self.flushRead()
|
|
104
|
+
|
|
105
|
+
if self._port.isOpen():
|
|
106
|
+
self._status = self.Status.CONNECTED
|
|
107
|
+
else:
|
|
108
|
+
self._status = self.Status.DISCONNECTED
|
|
109
|
+
|
|
69
110
|
self._logger.info("Adapter opened !")
|
|
111
|
+
if self._thread is None or not self._thread.is_alive():
|
|
112
|
+
self._start_thread()
|
|
70
113
|
|
|
71
114
|
def close(self):
|
|
72
115
|
super().close()
|
|
@@ -96,7 +139,7 @@ class SerialPort(Adapter):
|
|
|
96
139
|
"""
|
|
97
140
|
Start the read thread
|
|
98
141
|
"""
|
|
99
|
-
|
|
142
|
+
super()._start_thread()
|
|
100
143
|
if self._thread is None or not self._thread.is_alive():
|
|
101
144
|
self._thread = Thread(target=self._read_thread, daemon=True, args=(self._port, self._read_queue, self._thread_stop_read))
|
|
102
145
|
self._thread.start()
|
|
@@ -116,8 +159,8 @@ class SerialPort(Adapter):
|
|
|
116
159
|
else:
|
|
117
160
|
# Read data from the serialport with a timeout, if the timeout occurs, read again.
|
|
118
161
|
# This is to avoid having a crazy fast loop
|
|
119
|
-
|
|
120
|
-
if len(
|
|
162
|
+
fragment = port.read()
|
|
163
|
+
if len(fragment) > 0:
|
|
121
164
|
read_queue.put(fragment)
|
|
122
165
|
else:
|
|
123
166
|
ready, _, _ = select.select([self._port.fd, stop], [], [])
|
|
@@ -133,9 +176,9 @@ class SerialPort(Adapter):
|
|
|
133
176
|
else:
|
|
134
177
|
fragment = port.read(in_waiting)
|
|
135
178
|
if fragment:
|
|
136
|
-
read_queue.put(fragment)
|
|
179
|
+
read_queue.put(fragment)
|
|
137
180
|
|
|
138
|
-
def read(self, timeout
|
|
181
|
+
def read(self, timeout=..., stop_condition=..., return_metrics: bool = False) -> bytes:
|
|
139
182
|
"""
|
|
140
183
|
Read data from the device
|
|
141
184
|
|
|
@@ -157,26 +200,4 @@ class SerialPort(Adapter):
|
|
|
157
200
|
output = super().read(timeout, stop_condition, return_metrics)
|
|
158
201
|
if self._rts_cts: # Experimental
|
|
159
202
|
self._port.setRTS(False)
|
|
160
|
-
return output
|
|
161
|
-
|
|
162
|
-
def query(self, data : Union[bytes, str], timeout=None, stop_condition=None, return_metrics : bool = False):
|
|
163
|
-
self.flushRead()
|
|
164
|
-
self.write(data)
|
|
165
|
-
return self.read(timeout=timeout, stop_condition=stop_condition, return_metrics=return_metrics)
|
|
166
|
-
|
|
167
|
-
# def shell_parse(inp: str):
|
|
168
|
-
# parser = argparse.ArgumentParser(
|
|
169
|
-
# prog='',
|
|
170
|
-
# description='Serial port shell parser',
|
|
171
|
-
# epilog='')
|
|
172
|
-
# # Parse subcommand
|
|
173
|
-
# parser.add_argument('--' + shell.Arguments.PORT.value, type=str)
|
|
174
|
-
# parser.add_argument('--' + shell.Arguments.BAUDRATE.value, type=int)
|
|
175
|
-
# parser.add_argument('--' + shell.Arguments.ENABLE_RTS_CTS.value, action='store_true')
|
|
176
|
-
# args = parser.parse_args(inp.split())
|
|
177
|
-
|
|
178
|
-
# return {
|
|
179
|
-
# 'port' : getattr(args, shell.Arguments.PORT.value),
|
|
180
|
-
# 'baudrate' : getattr(args, shell.Arguments.BAUDRATE.value),
|
|
181
|
-
# 'rts_cts' : bool(getattr(args, shell.Arguments.ENABLE_RTS_CTS.value))
|
|
182
|
-
# }
|
|
203
|
+
return output
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Union, Tuple
|
|
1
|
+
from typing import Union, Tuple, Callable
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from time import time
|
|
4
4
|
|
|
@@ -140,6 +140,7 @@ class Termination(StopCondition):
|
|
|
140
140
|
def __str__(self) -> str:
|
|
141
141
|
return f'Termination({repr(self._termination)})'
|
|
142
142
|
|
|
143
|
+
# TODO : Add a "allow_longer" parameter ? If more than N data is received, keep it instead of raising an error ?
|
|
143
144
|
class Length(StopCondition):
|
|
144
145
|
def __init__(self, N : int) -> None:
|
|
145
146
|
"""
|
|
@@ -164,10 +165,46 @@ class Length(StopCondition):
|
|
|
164
165
|
deferred_fragment = data[remaining_bytes:]
|
|
165
166
|
self._counter += len(kept_fragment)
|
|
166
167
|
remaining_bytes = self._N - self._counter
|
|
168
|
+
# TODO : remaining_bytes <= 0 ? Alongside above TODO maybe
|
|
167
169
|
return remaining_bytes == 0, kept_fragment, deferred_fragment
|
|
168
170
|
|
|
169
171
|
def __repr__(self) -> str:
|
|
170
172
|
return self.__str__()
|
|
171
173
|
|
|
172
174
|
def __str__(self) -> str:
|
|
173
|
-
return f'Length({self._N})'
|
|
175
|
+
return f'Length({self._N})'
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# class Lambda(StopCondition):
|
|
180
|
+
# class LambdaReturn:
|
|
181
|
+
# ERROR = 'error' # Discard everything and raise an error
|
|
182
|
+
# VALID = 'valid' # Return everything
|
|
183
|
+
# KEEP_N = 'keep_n' # Keep the first N bytes
|
|
184
|
+
# CONTINUE = 'continue' # Keep reading
|
|
185
|
+
|
|
186
|
+
# def __init__(self, _lambda : Callable) -> None:
|
|
187
|
+
# super().__init__()
|
|
188
|
+
# self._lambda = _lambda
|
|
189
|
+
|
|
190
|
+
# def initiate_read(self) -> Union[float, None]:
|
|
191
|
+
# return None
|
|
192
|
+
|
|
193
|
+
# def evaluate(self, fragment: bytes) -> Tuple[bool, Union[float, None]]:
|
|
194
|
+
# lambda_return, N = self._lambda(fragment)
|
|
195
|
+
# match lambda_return:
|
|
196
|
+
# case self.LambdaReturn.ERROR:
|
|
197
|
+
# raise RuntimeError(f"Couldn't apply Lambda condition on fragment : {fragment}")
|
|
198
|
+
# case self.LambdaReturn.VALID:
|
|
199
|
+
# return True, fragment, b''
|
|
200
|
+
# case self.LambdaReturn.KEEP_N:
|
|
201
|
+
# return True, fragment[:N], fragment[N:]
|
|
202
|
+
# case self.LambdaReturn.CONTINUE:
|
|
203
|
+
# return False, fragment, b'' # TODO : Check this
|
|
204
|
+
|
|
205
|
+
# def __repr__(self) -> str:
|
|
206
|
+
# return self.__str__()
|
|
207
|
+
|
|
208
|
+
# def __str__(self) -> str:
|
|
209
|
+
# return f'Lambda(...)'
|
|
210
|
+
|