syndesi 0.1.5__py3-none-any.whl → 0.2.0__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 (41) hide show
  1. syndesi/adapters/__init__.py +7 -3
  2. syndesi/adapters/adapter.py +321 -0
  3. syndesi/adapters/auto.py +45 -0
  4. syndesi/adapters/iadapter.py +158 -20
  5. syndesi/adapters/ip.py +106 -40
  6. syndesi/adapters/ip_server.py +108 -0
  7. syndesi/adapters/proxy.py +92 -0
  8. syndesi/adapters/remote.py +18 -0
  9. syndesi/adapters/serialport.py +158 -0
  10. syndesi/adapters/stop_conditions.py +173 -0
  11. syndesi/adapters/timed_queue.py +32 -0
  12. syndesi/adapters/timeout.py +297 -0
  13. syndesi/adapters/visa.py +10 -8
  14. syndesi/api/__init__.py +0 -0
  15. syndesi/api/api.py +77 -0
  16. syndesi/protocols/__init__.py +2 -2
  17. syndesi/protocols/delimited.py +34 -22
  18. syndesi/protocols/iprotocol.py +2 -2
  19. syndesi/protocols/protocol.py +17 -0
  20. syndesi/protocols/raw.py +9 -60
  21. syndesi/protocols/scpi.py +37 -16
  22. syndesi/protocols/sdp.py +4 -4
  23. syndesi/proxy/__init__.py +0 -0
  24. syndesi/proxy/proxy.py +136 -0
  25. syndesi/proxy/proxy_api.py +93 -0
  26. syndesi/tools/log.py +107 -0
  27. syndesi/tools/logger.py +113 -0
  28. syndesi/tools/others.py +1 -0
  29. syndesi/tools/remote_api.py +113 -0
  30. syndesi/tools/remote_server.py +133 -0
  31. syndesi/tools/shell.py +111 -0
  32. syndesi/tools/types.py +24 -18
  33. {syndesi-0.1.5.dist-info → syndesi-0.2.0.dist-info}/LICENSE +0 -0
  34. {syndesi-0.1.5.dist-info → syndesi-0.2.0.dist-info}/METADATA +17 -15
  35. syndesi-0.2.0.dist-info/RECORD +63 -0
  36. syndesi-0.2.0.dist-info/entry_points.txt +3 -0
  37. syndesi-0.2.0.dist-info/top_level.txt +1 -0
  38. syndesi-0.1.5.data/scripts/syndesi +0 -1
  39. syndesi-0.1.5.dist-info/RECORD +0 -42
  40. syndesi-0.1.5.dist-info/top_level.txt +0 -2
  41. {syndesi-0.1.5.dist-info → syndesi-0.2.0.dist-info}/WHEEL +0 -0
syndesi/adapters/ip.py CHANGED
@@ -1,34 +1,79 @@
1
-
2
1
  import socket
3
2
  from enum import Enum
4
- from .iadapter import IAdapter
5
- from ..tools.types import assert_byte_instance
3
+ from .adapter import Adapter
4
+ from ..tools.types import to_bytes
5
+ from .timeout import Timeout
6
+ from threading import Thread
7
+ from .timed_queue import TimedQueue
8
+ from typing import Union
9
+ from time import time
10
+ import argparse
11
+ from ..tools import shell
12
+
13
+ class IP(Adapter):
14
+ DEFAULT_RESPONSE_TIMEOUT = 1
15
+ DEFAULT_CONTINUATION_TIMEOUT = 1e-3
16
+ DEFAULT_TOTAL_TIMEOUT = 5
17
+
6
18
 
7
- class IP(IAdapter):
8
- class Status(Enum):
9
- DISCONNECTED = 0
10
- CONNECTED = 1
19
+ DEFAULT_TIMEOUT = Timeout(
20
+ response=DEFAULT_RESPONSE_TIMEOUT,
21
+ continuation=DEFAULT_CONTINUATION_TIMEOUT,
22
+ total=DEFAULT_TOTAL_TIMEOUT)
23
+ DEFAULT_BUFFER_SIZE = 1024
11
24
  class Protocol(Enum):
12
- TCP = 0
13
- UDP = 1
14
- def __init__(self, descriptor : str, port = None, transport : Protocol = Protocol.TCP):
25
+ TCP = 'TCP'
26
+ UDP = 'UDP'
27
+
28
+ def __init__(self,
29
+ address : str,
30
+ port : int = None,
31
+ transport : str = 'TCP',
32
+ timeout : Union[Timeout, float] = DEFAULT_TIMEOUT,
33
+ stop_condition = None,
34
+ alias : str = '',
35
+ buffer_size : int = DEFAULT_BUFFER_SIZE,
36
+ _socket : socket.socket = None):
15
37
  """
16
38
  IP stack adapter
17
39
 
18
40
  Parameters
19
41
  ----------
20
- descriptor : str
42
+ address : str
21
43
  IP description
22
44
  port : int
23
- port used (optional, can be specified in descriptor)
24
- transport : Transport
25
- Transport protocol, TCP or UDP
45
+ IP port
46
+ transport : str
47
+ 'TCP' or 'UDP'
48
+ timeout : Timeout | float
49
+ Specify communication timeout
50
+ stop_condition : StopCondition
51
+ Specify a read stop condition (None by default)
52
+ alias : str
53
+ Specify an alias for this adapter, '' by default
54
+ buffer_size : int
55
+ Socket buffer size, may be removed in the future
56
+ socket : socket.socket
57
+ Specify a custom socket, this is reserved for server application
26
58
  """
27
- self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
28
- self._socket.settimeout(5)
29
- self._ip = descriptor # TODO : update this
59
+ super().__init__(alias=alias, timeout=timeout, stop_condition=stop_condition)
60
+ self._transport = self.Protocol(transport)
61
+ self._is_server = _socket is not None
62
+
63
+ self._logger.info(f"Setting up {self._transport.value} IP adapter ({'server' if self._is_server else 'client'})")
64
+
65
+ if self._is_server:
66
+ # Server
67
+ self._socket = _socket
68
+ self._status = self.Status.CONNECTED
69
+ elif self._transport == self.Protocol.TCP:
70
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
71
+ elif self._transport == self.Protocol.UDP:
72
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
73
+
74
+ self._address = address
30
75
  self._port = port
31
- self._status = self.Status.DISCONNECTED
76
+ self._buffer_size = buffer_size
32
77
 
33
78
  def set_default_port(self, port):
34
79
  """
@@ -44,41 +89,62 @@ class IP(IAdapter):
44
89
  if self._port is None:
45
90
  self._port = port
46
91
 
47
-
48
- def flushRead(self):
49
- return super().flushRead()
50
-
51
92
  def open(self):
52
- self._socket.connect((self._ip, self._port))
93
+ if self._is_server:
94
+ raise SystemError("Cannot open server socket. It must be passed already opened")
95
+ if self._port is None:
96
+ raise ValueError(f"Cannot open adapter without specifying a port")
97
+
98
+ self._logger.debug(f"Adapter {self._alias} connect to ({self._address}, {self._port})")
99
+ self._socket.connect((self._address, self._port))
53
100
  self._status = self.Status.CONNECTED
101
+ self._logger.info(f"Adapter {self._alias} opened !")
54
102
 
55
103
  def close(self):
56
- self._socket.close()
104
+ if hasattr(self, '_socket'):
105
+ self._socket.close()
106
+ self._logger.info("Adapter closed !")
107
+ self._status = self.Status.DISCONNECTED
57
108
 
58
- def write(self, data : bytes):
59
- assert_byte_instance(data)
109
+ def write(self, data : Union[bytes, str]):
110
+ data = to_bytes(data)
60
111
  if self._status == self.Status.DISCONNECTED:
112
+ self._logger.info(f"Adapter {self._alias} is closed, opening...")
61
113
  self.open()
114
+ write_start = time()
62
115
  self._socket.send(data)
116
+ write_duration = time() - write_start
117
+ self._logger.debug(f"Written [{write_duration*1e3:.3f}ms]: {repr(data)}")
63
118
 
64
- def read(self):
65
- if self._status == self.Status.DISCONNECTED:
66
- self.open()
119
+ def _start_thread(self):
120
+ self._logger.debug("Starting read thread...")
121
+ self._thread = Thread(target=self._read_thread, daemon=True, args=(self._socket, self._read_queue))
122
+ self._thread.start()
123
+
124
+ # EXPERIMENTAL
125
+ def read_thread_alive(self):
126
+ return self._thread.is_alive()
67
127
 
68
- self._socket.settimeout(10)
69
128
 
70
- buffer = b''
71
- while True:
129
+ def _read_thread(self, socket : socket.socket, read_queue : TimedQueue):
130
+ while True: # TODO : Add stop_pipe ? Maybe it was removed ?
72
131
  try:
73
- recv = self._socket.recv(10)
74
- self._socket.settimeout(0.05)
75
- buffer += recv
76
- except socket.timeout as e:
132
+ payload = socket.recv(self._buffer_size)
133
+ if len(payload) == self._buffer_size and self._transport == self.Protocol.UDP:
134
+ self._logger.warning("Warning, inbound UDP data may have been lost (max buffer size attained)")
135
+ except OSError:
77
136
  break
78
- return buffer
137
+ # If payload is empty, it means the socket has been disconnected
138
+ if payload == b'':
139
+ read_queue.put(payload)
140
+ break
141
+ read_queue.put(payload)
79
142
 
80
- def query(self, data : bytes):
143
+ def query(self, data : Union[bytes, str], timeout=None, stop_condition=None, return_metrics : bool = False):
144
+ if self._is_server:
145
+ raise SystemError("Cannot query on server adapters")
81
146
  self.flushRead()
82
147
  self.write(data)
83
- return self.read()
84
-
148
+ return self.read(timeout=timeout, stop_condition=stop_condition, return_metrics=return_metrics)
149
+
150
+
@@ -0,0 +1,108 @@
1
+ # ip_server.py
2
+ # Sébastien Deriaz
3
+ # 31.05.2024
4
+
5
+ import socket
6
+ from enum import Enum
7
+ from . import IP, StopCondition, Timeout
8
+ from time import time
9
+ from ..tools.types import to_bytes
10
+ from .timeout import timeout_fuse
11
+ import logging
12
+ from ..tools.log import LoggerAlias
13
+
14
+ # NOTE : The adapter is only meant to work with a single client, thus
15
+ # a IPServer class is made to handle the socket and generate IP adapters
16
+
17
+ class IPServer:
18
+ DEFAULT_BUFFER_SIZE = 1024
19
+ class Protocol(Enum):
20
+ TCP = 'TCP'
21
+ UDP = 'UDP'
22
+
23
+ def __init__(self,
24
+ port : int = None,
25
+ transport : str = 'TCP',
26
+ address : str = None,
27
+ max_clients : int = 5,
28
+ stop_condition = None,
29
+ alias : str = '',
30
+ buffer_size : int = DEFAULT_BUFFER_SIZE):
31
+
32
+ """
33
+ IP server adapter
34
+
35
+ Parameters
36
+ ----------
37
+ port : int
38
+ IP port. If None, the default port set with set_default_port will be used
39
+ transport : str
40
+ 'TCP' or 'UDP'
41
+ address : str
42
+ Custom socket ip, None by default
43
+ max_clients : int
44
+ Maximum number of clients, 5 by default
45
+ stop_condition : StopCondition
46
+ Specify a read stop condition (None by default)
47
+ alias : str
48
+ Specify an alias for this adapter, '' by default
49
+ buffer_size : int
50
+ Socket buffer size, may be removed in the future
51
+ """
52
+ self._alias = alias
53
+ self._stop_condition = stop_condition
54
+ self._logger = logging.getLogger(LoggerAlias.ADAPTER.value)
55
+ self._transport = self.Protocol(transport)
56
+ if self._transport == self.Protocol.TCP:
57
+ self._logger.info("Setting up TCP IP server adapter")
58
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
59
+ elif self._transport == self.Protocol.UDP:
60
+ self._logger.info("Setting up UDP IP server adapter")
61
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
62
+ else:
63
+ raise ValueError("Invalid protocol")
64
+
65
+ self._address = socket.gethostname() if address is None else address
66
+ self._port = port
67
+ self._max_clients = max_clients
68
+ self._opened = False
69
+
70
+
71
+ def set_default_port(self, port):
72
+ """
73
+ Sets IP port if no port has been set yet.
74
+
75
+ This way, the user can leave the port empty
76
+ and the driver/protocol can specify it later
77
+
78
+ Parameters
79
+ ----------
80
+ port : int
81
+ """
82
+ if self._port is None:
83
+ self._port = port
84
+
85
+
86
+ def open(self):
87
+ if self._port is None:
88
+ raise ValueError(f"Cannot open adapter without specifying a port")
89
+ self._logger.info(f"Listening to incoming connections on {self._address}:{self._port}")
90
+ self._socket.bind((self._address, self._port))
91
+ self._socket.listen(self._max_clients)
92
+ self._opened = True
93
+
94
+ def close(self):
95
+ if hasattr(self, '_socket'):
96
+ self._socket.close()
97
+ self._logger.info("Adapter closed !")
98
+ self._opened = False
99
+
100
+ def get_client(self, stop_condition : StopCondition = None, timeout : Timeout = None) -> IP:
101
+ """
102
+ Wait for a client to connect to the server and return the corresponding adapter
103
+ """
104
+ if not self._opened:
105
+ raise RuntimeError("open() must be called before getting client")
106
+ client_socket, address = self._socket.accept()
107
+ default_timeout = Timeout(response=None, continuation=IP.DEFAULT_CONTINUATION_TIMEOUT, total=None)
108
+ return IP(_socket=client_socket, address=address, stop_condition=stop_condition, timeout=timeout_fuse(timeout, default_timeout))
@@ -0,0 +1,92 @@
1
+ # proxy.py
2
+ # Sébastien Deriaz
3
+ # 09.04.2024
4
+ #
5
+ # The proxy adapter allows for commands to be issued on a different device
6
+ # The goal is to istanciate a class as such :
7
+ #
8
+ # Only adapter :
9
+ # # The proxy computer is accessed with 192.168.1.1
10
+ # # The device (connected to the remote computer) is accessed with 192.168.2.1
11
+ # my_adapter = Proxy('192.168.1.1', IP('192.168.2.1'))
12
+ #
13
+ # Protocol :
14
+ # my_protocol = SCPI(Proxy('192.168.1.1', Serial('/dev/ttyUSB0')))
15
+ #
16
+ #
17
+ # Driver :
18
+ # my_device = Driver(Proxy('192.168.1.1', VISA('...')))
19
+
20
+ from enum import Enum
21
+ from typing import Union
22
+
23
+ from .adapter import Adapter
24
+ from . import IP, SerialPort, VISA
25
+ from ..proxy.proxy_api import *
26
+ from ..api.api import parse
27
+
28
+ DEFAULT_PORT = 2608
29
+
30
+ class Proxy(Adapter):
31
+ def __init__(self, proxy_adapter : Adapter, remote_adapter : Adapter):
32
+ """
33
+ Proxy adapter
34
+
35
+ Parameters
36
+ ----------
37
+ proxy_adapter : Adapter
38
+ Adapter to connect to the proxy server
39
+ remote_adapter : Adapter
40
+ Adapter to instanciate onto the proxy server
41
+ """
42
+ super().__init__()
43
+
44
+ self._proxy = proxy_adapter
45
+ self._remote = remote_adapter
46
+
47
+ if isinstance(proxy_adapter, IP):
48
+ proxy_adapter.set_default_port(DEFAULT_PORT)
49
+
50
+ if isinstance(self._remote, IP):
51
+ self._proxy.query(IPInstanciate(
52
+ address=self._remote._address,
53
+ port=self._remote._port,
54
+ transport=self._remote._transport,
55
+ buffer_size=self._remote._buffer_size
56
+ ).encode())
57
+ elif isinstance(self._remote, SerialPort):
58
+ self._proxy.query()
59
+
60
+ def check(self, status : ReturnStatus):
61
+ if not status.success:
62
+ # There is an error
63
+ raise ProxyException(status.error_message)
64
+
65
+ def open(self):
66
+ if isinstance(self._remote, IP):
67
+ self._proxy.query(AdapterOpen().encode())
68
+
69
+ def close(self):
70
+ self._proxy.query(AdapterClose().encode())
71
+
72
+ def write(self, data : Union[bytes, str]):
73
+ self._proxy.query(AdapterWrite(data).encode())
74
+
75
+ def read(self, timeout=None, stop_condition=None, return_metrics : bool = False):
76
+ output : AdapterReadReturn
77
+ output = parse(self._proxy.query(AdapterRead().encode()))
78
+ if isinstance(output, AdapterReadReturn):
79
+ return output.data
80
+ elif isinstance(output, ReturnStatus):
81
+ raise ProxyException(output.error_message)
82
+ else:
83
+ raise RuntimeError(f"Invalid return : {type(output)}")
84
+
85
+
86
+ def query(self, data : Union[bytes, str], timeout=None, stop_condition=None, return_metrics : bool = False):
87
+ self.check(parse(self._proxy.query(AdapterFlushRead().encode())))
88
+ self.check(parse(self._proxy.query(AdapterWrite(data).encode())))
89
+ return self.read(timeout=timeout, stop_condition=stop_condition, return_metrics=return_metrics)
90
+
91
+ def _start_thread(self):
92
+ pass
@@ -0,0 +1,18 @@
1
+ # remote.py
2
+ # Sébastien Deriaz
3
+ # 09.04.2024
4
+ #
5
+ # The remote adapter allows for commands to be issued on a different device through TCP
6
+ # The goal is to istanciate a class as such :
7
+ #
8
+ # Only adapter :
9
+ # # The remote computer is accessed with 192.168.1.1
10
+ # # The device (connected to the remote computer) is accessed with 192.168.2.1
11
+ # my_adapter = Remote('192.168.1.1', IP('192.168.2.1'))
12
+ #
13
+ # Protocol :
14
+ # my_protocol = SCPI(Remote('192.168.1.1', Serial('/dev/ttyUSB0')))
15
+ #
16
+ #
17
+ # Driver :
18
+ # my_device = Driver(Remote('192.168.1.1', VISA('...')))
@@ -0,0 +1,158 @@
1
+ import os
2
+ import serial
3
+ from threading import Thread
4
+ from typing import Union
5
+ import select
6
+ import argparse
7
+ #from collections.abc import Sequence
8
+
9
+ from .adapter import Adapter
10
+ from ..tools.types import to_bytes
11
+ from .stop_conditions import *
12
+ from .timeout import Timeout
13
+ from .timed_queue import TimedQueue
14
+ from ..tools import shell
15
+ from ..tools.others import DEFAULT
16
+
17
+ # From pyserial - serialposix.py
18
+ import fcntl
19
+ import termios
20
+ import struct
21
+ if hasattr(termios, 'TIOCINQ'):
22
+ TIOCINQ = termios.TIOCINQ
23
+ else:
24
+ TIOCINQ = getattr(termios, 'FIONREAD', 0x541B)
25
+ TIOCM_zero_str = struct.pack('I', 0)
26
+
27
+ DEFAULT_TIMEOUT = Timeout(response=1, continuation=200e-3, total=None)
28
+
29
+ class SerialPort(Adapter):
30
+ def __init__(self,
31
+ port : str,
32
+ baudrate : int,
33
+ timeout : Union[Timeout, float] = DEFAULT,
34
+ stop_condition : StopCondition = DEFAULT,
35
+ rts_cts : bool = False): # rts_cts experimental
36
+ """
37
+ Serial communication adapter
38
+
39
+ Parameters
40
+ ----------
41
+ port : str
42
+ Serial port (COMx or ttyACMx)
43
+ """
44
+ if timeout == DEFAULT:
45
+ timeout = DEFAULT_TIMEOUT
46
+
47
+ super().__init__(timeout=timeout, stop_condition=stop_condition)
48
+ self._logger.info(f"Setting up SerialPort adapter timeout:{timeout}, stop_condition:{stop_condition}")
49
+ self._port = serial.Serial(port=port, baudrate=baudrate)
50
+ if self._port.isOpen():
51
+ self._status = self.Status.CONNECTED
52
+ else:
53
+ self._status = self.Status.DISCONNECTED
54
+
55
+ self._rts_cts = rts_cts
56
+
57
+ self._stop_event_pipe, self._stop_event_pipe_write = os.pipe()
58
+
59
+ def flushRead(self):
60
+ self._port.flush()
61
+
62
+ def open(self):
63
+ self._port.open()
64
+ # Flush the input buffer
65
+ buf = b'0'
66
+ while buf:
67
+ buf = os.read(self._port.fd)
68
+ self._logger.info("Adapter opened !")
69
+
70
+ def close(self):
71
+ if self._thread is not None and self._thread.is_alive():
72
+ os.write(self._stop_event_pipe_write, b'1')
73
+ self._thread.join()
74
+ if hasattr(self, '_port'):
75
+ self._port.close()
76
+ self._logger.info("Adapter closed !")
77
+
78
+ def write(self, data : bytes):
79
+ if self._rts_cts: # Experimental
80
+ self._port.setRTS(True)
81
+ data = to_bytes(data)
82
+ if self._status == self.Status.DISCONNECTED:
83
+ self.open()
84
+ write_start = time()
85
+ self._port.write(data)
86
+ write_duration = time() - write_start
87
+ self._logger.debug(f"Written [{write_duration*1e3:.3f}ms]: {repr(data)}")
88
+
89
+ def _start_thread(self):
90
+ """
91
+ Start the read thread
92
+ """
93
+ self._logger.debug("Starting read thread...")
94
+ if self._thread is None or not self._thread.is_alive():
95
+ self._thread = Thread(target=self._read_thread, daemon=True, args=(self._port, self._read_queue, self._stop_event_pipe))
96
+ self._thread.start()
97
+
98
+ def _read_thread(self, port : serial.Serial , read_queue : TimedQueue, stop_event_pipe):
99
+ while True:
100
+ # It looks like using the raw implementation of port.in_waiting and port.read is better, there's no more warnings
101
+ # Equivalent of port.in_waiting :
102
+ #in_waiting = struct.unpack('I', fcntl.ioctl(port.fd, TIOCINQ, TIOCM_zero_str))[0]
103
+ in_waiting = self._port.in_waiting # This is a temporary fix to get windows compatiblity back, an error might pop up
104
+ if in_waiting == 0:
105
+ ready, _, _ = select.select([port.fd, stop_event_pipe], [], [], None)
106
+ if stop_event_pipe in ready:
107
+ # Stop
108
+ break
109
+ # Else, read as many bytes as possible
110
+ fragment = os.read(port.fd, 1000) # simplified version of port.read()
111
+ if fragment:
112
+ read_queue.put(fragment)
113
+
114
+ def read(self, timeout=None, stop_condition=None, return_metrics: bool = False) -> bytes:
115
+ """
116
+ Read data from the device
117
+
118
+ Parameters
119
+ ----------
120
+ timeout : Timeout or None
121
+ Set a custom timeout, if None (default), the adapter timeout is used
122
+ stop_condition : StopCondition or None
123
+ Set a custom stop condition, if None (Default), the adapater stop condition is used
124
+ return_metrics : bool
125
+ Return a dictionary containing information about the read operation like
126
+ 'read_duration' : float
127
+ 'origin' : 'timeout' or 'stop_condition'
128
+ 'timeout' : Timeout.TimeoutType
129
+ 'stop_condition' : Length or Termination (StopCondition class)
130
+ 'previous_read_buffer_used' : bool
131
+ 'n_fragments' : int
132
+ """
133
+ output = super().read(timeout, stop_condition, return_metrics)
134
+ if self._rts_cts: # Experimental
135
+ self._port.setRTS(False)
136
+ return output
137
+
138
+ def query(self, data : Union[bytes, str], timeout=None, stop_condition=None, return_metrics : bool = False):
139
+ self.flushRead()
140
+ self.write(data)
141
+ return self.read(timeout=timeout, stop_condition=stop_condition, return_metrics=return_metrics)
142
+
143
+ def shell_parse(inp: str):
144
+ parser = argparse.ArgumentParser(
145
+ prog='',
146
+ description='Serial port shell parser',
147
+ epilog='')
148
+ # Parse subcommand
149
+ parser.add_argument('--' + shell.Arguments.PORT.value, type=str)
150
+ parser.add_argument('--' + shell.Arguments.BAUDRATE.value, type=int)
151
+ parser.add_argument('--' + shell.Arguments.ENABLE_RTS_CTS.value, action='store_true')
152
+ args = parser.parse_args(inp.split())
153
+
154
+ return {
155
+ 'port' : getattr(args, shell.Arguments.PORT.value),
156
+ 'baudrate' : getattr(args, shell.Arguments.BAUDRATE.value),
157
+ 'rts_cts' : bool(getattr(args, shell.Arguments.ENABLE_RTS_CTS.value))
158
+ }