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.
- {syndesi-0.4.0/syndesi.egg-info → syndesi-0.4.1}/PKG-INFO +1 -1
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/adapter.py +52 -123
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/auto.py +1 -1
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/adapter_backend.py +0 -1
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/adapter_session.py +2 -2
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/descriptors.py +3 -2
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/ip_backend.py +1 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/serialport_backend.py +5 -5
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/visa_backend.py +7 -7
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/ip.py +6 -10
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/timeout.py +1 -1
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/visa.py +2 -2
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/delimited.py +16 -21
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/modbus.py +17 -14
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/raw.py +20 -16
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/scpi.py +17 -15
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/backend_api.py +1 -1
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/version.py +1 -1
- {syndesi-0.4.0 → syndesi-0.4.1/syndesi.egg-info}/PKG-INFO +1 -1
- {syndesi-0.4.0 → syndesi-0.4.1}/LICENSE +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/README.md +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/pyproject.toml +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/setup.cfg +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/__init__.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/__main__.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/__init__.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/__init__.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/adapter_manager.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/backend.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/backend_tools.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/stop_condition_backend.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/timed_queue.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/backend/timeout.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/ip_server.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/serialport.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/adapters/stop_condition.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/__init__.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/backend_console.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/backend_status.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/backend_wrapper.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/console.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/shell.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/shell_tools.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/terminal.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/terminal_apps.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/cli/terminal_tools.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/__init__.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/protocols/protocol.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/scripts/__init__.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/scripts/syndesi.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/scripts/syndesi_backend.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/__init__.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/backend_logger.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/errors.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/exceptions.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/internal.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/log.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/log_settings.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi/tools/types.py +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi.egg-info/SOURCES.txt +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi.egg-info/dependency_links.txt +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi.egg-info/entry_points.txt +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi.egg-info/requires.txt +0 -0
- {syndesi-0.4.0 → syndesi-0.4.1}/syndesi.egg-info/top_level.txt +0 -0
|
@@ -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.
|
|
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
|
-
|
|
466
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
466
467
|
scope : str = ReadScope.BUFFERED.value,
|
|
467
|
-
) -> AdapterReadPayload
|
|
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
|
|
504
|
-
|
|
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
|
-
|
|
516
|
+
_response = read_timeout.response()
|
|
508
517
|
|
|
509
|
-
|
|
510
|
-
|
|
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
|
-
|
|
521
|
-
|
|
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.
|
|
550
|
-
|
|
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
|
-
|
|
580
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
646
581
|
) -> bytes:
|
|
647
|
-
signal = self.read_detailed(timeout=timeout,
|
|
648
|
-
|
|
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
|
-
|
|
662
|
-
) ->
|
|
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,
|
|
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
|
-
|
|
609
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
678
610
|
) -> bytes:
|
|
679
611
|
signal = self.query_detailed(
|
|
680
|
-
data=data, timeout=timeout,
|
|
612
|
+
data=data, timeout=timeout, stop_conditions=stop_conditions
|
|
681
613
|
)
|
|
682
|
-
|
|
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)
|
|
@@ -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.
|
|
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.
|
|
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,
|
|
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}"
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import time
|
|
8
8
|
|
|
9
|
-
import serial
|
|
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
|
|
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
|
|
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
|
|
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 = ""
|
|
109
|
-
self._inst.read_termination = None
|
|
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)
|
|
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)
|
|
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 =
|
|
31
|
+
transport: str = DEFAULT_PROTOCOL.value,
|
|
32
32
|
timeout: Timeout | NumberLike | None | EllipsisType = ...,
|
|
33
|
-
stop_conditions: StopCondition |
|
|
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
|
-
|
|
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
|
|
|
@@ -20,7 +20,7 @@ class Visa(Adapter):
|
|
|
20
20
|
self,
|
|
21
21
|
descriptor: str,
|
|
22
22
|
alias: str = "",
|
|
23
|
-
|
|
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=
|
|
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
|
-
|
|
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
|
|
131
|
+
return self.read(timeout=timeout)
|
|
134
132
|
|
|
135
133
|
def read_raw(
|
|
136
134
|
self,
|
|
137
135
|
timeout: Timeout | None | EllipsisType = ...,
|
|
138
|
-
|
|
139
|
-
) ->
|
|
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
|
-
|
|
155
|
-
timeout=timeout,
|
|
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
|
-
|
|
164
|
-
) ->
|
|
165
|
-
|
|
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
|
-
|
|
174
|
-
decode: bool = True,
|
|
175
|
-
full_output: bool = False,
|
|
168
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...
|
|
176
169
|
) -> str:
|
|
177
|
-
|
|
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
|
-
|
|
398
|
-
self._length(n_coil_bytes + 2)
|
|
399
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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),
|
|
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),
|
|
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),
|
|
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),
|
|
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),
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
76
|
-
) ->
|
|
79
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
80
|
+
) -> AdapterReadPayload:
|
|
77
81
|
return self._adapter.read_detailed(
|
|
78
|
-
timeout=timeout,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
119
|
-
) ->
|
|
120
|
-
|
|
118
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
119
|
+
) -> bytes:
|
|
120
|
+
signal = self.read_detailed(
|
|
121
121
|
timeout=timeout,
|
|
122
|
-
|
|
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
|
-
|
|
129
|
-
) ->
|
|
129
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
130
|
+
) -> AdapterReadPayload:
|
|
130
131
|
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
138
|
+
stop_conditions: StopCondition | EllipsisType | list[StopCondition] = ...,
|
|
140
139
|
) -> str:
|
|
141
|
-
|
|
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
|
-
|
|
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"
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|