syndesi 0.2.0__tar.gz → 0.2.2__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.
Files changed (39) hide show
  1. {syndesi-0.2.0 → syndesi-0.2.2}/LICENSE +0 -0
  2. {syndesi-0.2.0/syndesi.egg-info → syndesi-0.2.2}/PKG-INFO +1 -1
  3. {syndesi-0.2.0 → syndesi-0.2.2}/setup.py +1 -1
  4. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/proxy.py +1 -1
  5. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/serialport.py +27 -40
  6. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/stop_conditions.py +13 -1
  7. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/timeout.py +9 -0
  8. syndesi-0.2.2/syndesi/api/api.py +77 -0
  9. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/protocols/delimited.py +23 -36
  10. syndesi-0.2.2/syndesi/proxy/__init__.py +0 -0
  11. syndesi-0.2.2/syndesi/proxy/proxy.py +136 -0
  12. syndesi-0.2.2/syndesi/proxy/proxy_api.py +93 -0
  13. syndesi-0.2.2/syndesi/tools/__init__.py +0 -0
  14. {syndesi-0.2.0 → syndesi-0.2.2/syndesi.egg-info}/PKG-INFO +1 -1
  15. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi.egg-info/SOURCES.txt +5 -0
  16. {syndesi-0.2.0 → syndesi-0.2.2}/README.md +0 -0
  17. {syndesi-0.2.0 → syndesi-0.2.2}/setup.cfg +0 -0
  18. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/__init__.py +0 -0
  19. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/__init__.py +0 -0
  20. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/adapter.py +0 -0
  21. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/auto.py +0 -0
  22. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/ip.py +0 -0
  23. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/ip_server.py +0 -0
  24. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/timed_queue.py +0 -0
  25. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/adapters/visa.py +0 -0
  26. {syndesi-0.2.0/syndesi/tools → syndesi-0.2.2/syndesi/api}/__init__.py +0 -0
  27. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/protocols/__init__.py +0 -0
  28. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/protocols/protocol.py +0 -0
  29. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/protocols/raw.py +0 -0
  30. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/protocols/scpi.py +0 -0
  31. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/protocols/sdp.py +0 -0
  32. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/tools/exceptions.py +0 -0
  33. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/tools/log.py +0 -0
  34. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/tools/others.py +0 -0
  35. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/tools/shell.py +0 -0
  36. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi/tools/types.py +0 -0
  37. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi.egg-info/dependency_links.txt +0 -0
  38. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi.egg-info/entry_points.txt +0 -0
  39. {syndesi-0.2.0 → syndesi-0.2.2}/syndesi.egg-info/top_level.txt +0 -0
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: syndesi
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Syndesi
5
5
  Author: Sebastien Deriaz
6
6
  Author-email: sebastien.deriaz1@gmail.com
@@ -1,6 +1,6 @@
1
1
  from setuptools import setup, find_packages
2
2
 
3
- VERSION = '0.2.0'
3
+ VERSION = '0.2.2'
4
4
  DESCRIPTION = 'Syndesi'
5
5
 
6
6
  with open("README.md", "r", encoding="utf-8") as fh:
@@ -21,7 +21,7 @@ from enum import Enum
21
21
  from typing import Union
22
22
 
23
23
  from .adapter import Adapter
24
- from .. import IP, SerialPort, VISA
24
+ from . import IP, SerialPort, VISA
25
25
  from ..proxy.proxy_api import *
26
26
  from ..api.api import parse
27
27
 
@@ -1,35 +1,27 @@
1
- from .adapter import Adapter
1
+ import os
2
2
  import serial
3
- from ..tools.types import to_bytes
4
- from .stop_conditions import *
5
- from .timeout import Timeout
6
- from .timed_queue import TimedQueue
7
3
  from threading import Thread
8
4
  from typing import Union
9
5
  import select
10
6
  import argparse
11
- from ..tools import shell
12
- from collections.abc import Sequence
7
+ #from collections.abc import Sequence
13
8
 
14
- # From pyserial - serialposix.py
15
- import fcntl
16
- import termios
17
- import struct
18
- if hasattr(termios, 'TIOCINQ'):
19
- TIOCINQ = termios.TIOCINQ
20
- else:
21
- TIOCINQ = getattr(termios, 'FIONREAD', 0x541B)
22
- TIOCM_zero_str = struct.pack('I', 0)
23
- import os
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
24
16
 
25
- DEFAULT_TIMEOUT = Timeout(response=0.5, continuation=10e-3, total=None)
17
+ DEFAULT_TIMEOUT = Timeout(response=1, continuation=200e-3, total=None)
26
18
 
27
19
  class SerialPort(Adapter):
28
20
  def __init__(self,
29
21
  port : str,
30
22
  baudrate : int,
31
- timeout : Union[Timeout, float] = DEFAULT_TIMEOUT,
32
- stop_condition : StopCondition = None,
23
+ timeout : Union[Timeout, float] = DEFAULT,
24
+ stop_condition : StopCondition = DEFAULT,
33
25
  rts_cts : bool = False): # rts_cts experimental
34
26
  """
35
27
  Serial communication adapter
@@ -39,8 +31,11 @@ class SerialPort(Adapter):
39
31
  port : str
40
32
  Serial port (COMx or ttyACMx)
41
33
  """
34
+ if timeout == DEFAULT:
35
+ timeout = DEFAULT_TIMEOUT
36
+
42
37
  super().__init__(timeout=timeout, stop_condition=stop_condition)
43
- self._logger.info("Setting up SerialPort adapter")
38
+ self._logger.info(f"Setting up SerialPort adapter timeout:{timeout}, stop_condition:{stop_condition}")
44
39
  self._port = serial.Serial(port=port, baudrate=baudrate)
45
40
  if self._port.isOpen():
46
41
  self._status = self.Status.CONNECTED
@@ -48,8 +43,6 @@ class SerialPort(Adapter):
48
43
  self._status = self.Status.DISCONNECTED
49
44
 
50
45
  self._rts_cts = rts_cts
51
-
52
- self._stop_event_pipe, self._stop_event_pipe_write = os.pipe()
53
46
 
54
47
  def flushRead(self):
55
48
  self._port.flush()
@@ -63,10 +56,8 @@ class SerialPort(Adapter):
63
56
  self._logger.info("Adapter opened !")
64
57
 
65
58
  def close(self):
66
- if self._thread is not None and self._thread.is_alive():
67
- os.write(self._stop_event_pipe_write, b'1')
68
- self._thread.join()
69
59
  if hasattr(self, '_port'):
60
+ # Close and the read thread will die by itself
70
61
  self._port.close()
71
62
  self._logger.info("Adapter closed !")
72
63
 
@@ -87,23 +78,19 @@ class SerialPort(Adapter):
87
78
  """
88
79
  self._logger.debug("Starting read thread...")
89
80
  if self._thread is None or not self._thread.is_alive():
90
- self._thread = Thread(target=self._read_thread, daemon=True, args=(self._port, self._read_queue, self._stop_event_pipe))
81
+ self._thread = Thread(target=self._read_thread, daemon=True, args=(self._port, self._read_queue))
91
82
  self._thread.start()
92
83
 
93
- def _read_thread(self, port : serial.Serial , read_queue : TimedQueue, stop_event_pipe):
84
+ def _read_thread(self, port : serial.Serial , read_queue : TimedQueue):
85
+ # NOTE : There should be some way to kill the thread, maybe check for an error on in_waiting but couldn't find it so far
94
86
  while True:
95
- # It looks like using the raw implementation of port.in_waiting and port.read is better, there's no more warnings
96
- # Equivalent of port.in_waiting :
97
- in_waiting = struct.unpack('I', fcntl.ioctl(port.fd, TIOCINQ, TIOCM_zero_str))[0]
98
- if in_waiting == 0:
99
- ready, _, _ = select.select([port.fd, stop_event_pipe], [], [], None)
100
- if stop_event_pipe in ready:
101
- # Stop
102
- break
103
- # Else, read as many bytes as possible
104
- fragment = os.read(port.fd, 1000) # simplified version of port.read()
105
- if fragment:
106
- read_queue.put(fragment)
87
+ # Check how many bytes are available
88
+ in_waiting = self._port.in_waiting # This is a temporary fix to get windows compatiblity back, an error might pop up
89
+ if in_waiting > 0:
90
+ # Read those bytes
91
+ fragment = port.read(in_waiting)
92
+ if fragment:
93
+ read_queue.put(fragment)
107
94
 
108
95
  def read(self, timeout=None, stop_condition=None, return_metrics: bool = False) -> bytes:
109
96
  """
@@ -134,6 +134,12 @@ class Termination(StopCondition):
134
134
  self._fragment_store = b''
135
135
  return output
136
136
 
137
+ def __repr__(self) -> str:
138
+ return self.__str__()
139
+
140
+ def __str__(self) -> str:
141
+ return f'Termination({repr(self._termination)})'
142
+
137
143
  class Length(StopCondition):
138
144
  def __init__(self, N : int) -> None:
139
145
  """
@@ -158,4 +164,10 @@ class Length(StopCondition):
158
164
  deferred_fragment = data[remaining_bytes:]
159
165
  self._counter += len(kept_fragment)
160
166
  remaining_bytes = self._N - self._counter
161
- return remaining_bytes == 0, kept_fragment, deferred_fragment
167
+ return remaining_bytes == 0, kept_fragment, deferred_fragment
168
+
169
+ def __repr__(self) -> str:
170
+ return self.__str__()
171
+
172
+ def __str__(self) -> str:
173
+ return f'Length({self._N})'
@@ -235,6 +235,15 @@ class Timeout():
235
235
  """
236
236
  return self._data_strategy, self._last_data_strategy_origin
237
237
 
238
+ def __str__(self) -> str:
239
+ response = f'r:{self._response:.3f}ms/{self._on_response},' if self._response is not None else ''
240
+ continuation = f'c:{self._continuation:.3f}ms/{self._on_continuation},' if self._continuation is not None else ''
241
+ total = f't:{self._total:.3f}ms/{self._on_total}' if self._total is not None else ''
242
+ return f'Timeout({response}{continuation}{total})'
243
+
244
+ def __repr__(self) -> str:
245
+ return self.__str__()
246
+
238
247
 
239
248
  class TimeoutException(Exception):
240
249
  def __init__(self, type : Timeout.TimeoutType) -> None:
@@ -0,0 +1,77 @@
1
+ import quopri
2
+ from typing import Tuple, Union, Dict
3
+ from enum import Enum
4
+ from dataclasses import dataclass, fields, Field
5
+ import json
6
+
7
+ APIS = {}
8
+
9
+ def register_api(apis : dict):
10
+ """
11
+ Register apis
12
+
13
+ Parameters
14
+ ----------
15
+ apis : dict
16
+ {'action' : APICall} class dictionary
17
+ """
18
+ APIS.update(apis)
19
+
20
+ ACTION_ATTRIBUTE = 'action'
21
+
22
+ class APIItem:
23
+ pass
24
+
25
+ class APICall:
26
+ action = ''
27
+ _keyword = ''
28
+
29
+ def encode(self) -> bytes:
30
+ cls_fields: Tuple[Field, ...] = fields(self)
31
+ data = {}
32
+
33
+ # Add action field
34
+ data[ACTION_ATTRIBUTE] = self.action
35
+ # Add other fields
36
+ for field in cls_fields:
37
+ field_data = getattr(self, field.name)
38
+ if isinstance(field_data, Enum):
39
+ entry = field_data.value
40
+ elif isinstance(field_data, bytes):
41
+ entry = quopri.encodestring(field_data).decode('ASCII')
42
+ elif isinstance(field_data, str):
43
+ entry = quopri.encodestring(field_data.encode('utf-8')).decode('ASCII')
44
+ else:
45
+ entry = field_data
46
+
47
+ data[field.name] = entry
48
+ return json.dumps(data)
49
+
50
+ def parse(data : Union[str, bytes]) -> APICall:
51
+ json_data = json.loads(data)
52
+ action = json_data[ACTION_ATTRIBUTE]
53
+ json_data.pop(ACTION_ATTRIBUTE)
54
+ arguments = json_data
55
+ # Find the right API call and return it
56
+ if action not in APIS:
57
+ raise RuntimeError(f"API action '{action}' not registered")
58
+
59
+ converted_arguments = {}
60
+ # Convert each argument according to the class field types
61
+ api_fields: Tuple[Field, ...] = fields(APIS[action])
62
+
63
+ for field in api_fields:
64
+ if field.name not in arguments:
65
+ raise RuntimeError(f"Field '{field.name}' missing from arguments")
66
+
67
+ if field.type == bytes:
68
+ # Convert back
69
+ converted_arguments[field.name] = quopri.decodestring(arguments[field.name])
70
+ elif field.type == str:
71
+ converted_arguments[field.name] = quopri.decodestring(arguments[field.name]).decode('utf-8')
72
+ elif field.type == Enum:
73
+ converted_arguments[field.name] = field.type(arguments[field.name])
74
+ else:
75
+ converted_arguments[field.name] = arguments[field.name]
76
+
77
+ return APIS[action](**converted_arguments)
@@ -1,12 +1,12 @@
1
1
  from .protocol import Protocol
2
- from ..adapters import Adapter
2
+ from ..adapters import Adapter, Timeout, Termination
3
3
  from ..tools.types import assert_byte_instance, assert_byte_instance
4
4
  from time import time
5
5
  import warnings
6
6
 
7
7
 
8
8
  class Delimited(Protocol):
9
- def __init__(self, adapter : Adapter, termination='\n', format_response=True) -> None:
9
+ def __init__(self, adapter : Adapter, termination='\n', format_response=True, encoding : str = 'utf-8', timeout : Timeout = None) -> None:
10
10
  """
11
11
  Protocol with delimiter, like LF, CR, etc... '\\n' is used by default
12
12
 
@@ -15,18 +15,21 @@ class Delimited(Protocol):
15
15
  Parameters
16
16
  ----------
17
17
  adapter : IAdapter
18
- end : bytes
18
+ termination : bytes
19
19
  Command termination, '\\n' by default
20
20
  format_response : bool
21
- Apply formatting to the response (i.e removing the termination)
21
+ Apply formatting to the response (i.e removing the termination), True by default
22
+ encoding : str
23
+ timeout : Timeout
24
+ None by default (default timeout)
22
25
  """
23
- super().__init__(adapter)
24
-
25
- # Temporary solution before implementing stop conditions
26
- self._buffer = ''
26
+ adapter.set_default_stop_condition(stop_condition=Termination(sequence=termination))
27
+ super().__init__(adapter, timeout=timeout)
27
28
 
28
29
  if not isinstance(termination, str):
29
30
  raise ValueError(f"end argument must be of type str, not {type(termination)}")
31
+
32
+ self._encoding = encoding
30
33
  self._termination = termination
31
34
  self._response_formatting = format_response
32
35
 
@@ -62,40 +65,24 @@ class Delimited(Protocol):
62
65
  self.write(command)
63
66
  return self.read()
64
67
 
65
- # Note : for later revisions of the delimited module, the buffer should be removed as the
66
- # adapter will take care of that using the stop conditions
67
- #
68
- # For now the delimited module will take care of it
69
- #
70
- # Stop conditions should also be added inside the delimited module (unclear yet how)
71
-
72
- def read(self, timeout=2) -> str:
68
+ def read(self, timeout : Timeout = None, decode : str = True) -> str:
73
69
  """
74
70
  Reads command and formats it as a str
71
+
72
+ Parameters
73
+ ----------
74
+ timeout : Timeout
75
+ decode : bool
76
+ Decode incoming data, True by default
75
77
  """
76
- if self._termination not in self._buffer:
77
- # Read the adapter only if there isn't a fragment already in the buffer
78
- start = time()
79
- while True:
80
- # Continuously read the adapter as long as no termination is caught
81
- data = self._from_bytes(self._adapter.read())
82
- self._buffer += data
83
- if self._termination in data or time() > start + timeout:
84
- break
85
78
 
86
79
  # Send up to the termination
87
- fragment, self._buffer = self._buffer.split(self._termination, maxsplit=1)
80
+ data = self._adapter.read(timeout=timeout)
81
+ if decode:
82
+ data = data.decode(self._encoding)
88
83
  if self._response_formatting:
89
84
  # Only send the fragment (no termination)
90
- return fragment
85
+ return data
91
86
  else:
92
87
  # Add the termination back in
93
- return fragment + self._termination
94
-
95
- def read_raw(self) -> bytes:
96
- """
97
- Returns the raw bytes instead of str
98
- """
99
- if len(self._buffer) > 0:
100
- warnings.warn("Warning : The buffer wasn't empty, standard (non raw) data is still in it")
101
- return self._adapter.read()
88
+ return data + self._termination
File without changes
@@ -0,0 +1,136 @@
1
+ # proxy.py
2
+ # Sébastien Deriaz
3
+ # 28.05.2024
4
+ import argparse
5
+ from enum import Enum
6
+ from ..adapters import SerialPort
7
+ from ..adapters.adapter import AdapterDisconnected
8
+ from ..adapters.proxy import DEFAULT_PORT
9
+ from ..adapters.ip_server import IPServer
10
+ from typing import Union
11
+ from .proxy_api import *
12
+ from ..api.api import *
13
+ import logging
14
+ from ..tools.log import LoggerAlias, set_log_stream
15
+
16
+ class AdapterType(Enum):
17
+ SERIAL = 'serial'
18
+ IP = 'ip'
19
+
20
+ DEFAULT_BAUDRATE = 115200
21
+
22
+ def main():
23
+ parser = argparse.ArgumentParser(
24
+ prog='syndesi-proxy',
25
+ description='Syndesi proxy server',
26
+ epilog='')
27
+ # Parse subcommand
28
+ parser.add_argument('-t', '--adapter_type', choices=[x.value for x in AdapterType], default=AdapterType.IP)
29
+ parser.add_argument('-p', '--port', type=int, default=DEFAULT_PORT, help='IP port')
30
+ parser.add_argument('-a', '--address', default=None, type=str, help='IP address or serial port')
31
+ parser.add_argument('-b', '--baudrate', type=int, default=DEFAULT_BAUDRATE, help='Serial baudrate')
32
+ parser.add_argument('-v', '--verbose', action='store_true')
33
+
34
+ args = parser.parse_args()
35
+
36
+ if args.verbose:
37
+ set_log_stream(True, 'DEBUG')
38
+
39
+ proxy_server = ProxyServer(adapter_type=args.adapter_type, port=args.port, address=args.address, baudrate=args.baudrate)
40
+
41
+ proxy_server.start()
42
+
43
+ class ProxyServer:
44
+ def __init__(self, adapter_type : AdapterType, port : Union[str, int], address : str, baudrate : int) -> None:
45
+ self._adapter_type = AdapterType(adapter_type)
46
+ self._adapter = None
47
+ self._port = port
48
+ self._address = address
49
+ self._baudrate = baudrate
50
+ self._logger = logging.getLogger(LoggerAlias.PROXY_SERVER.value)
51
+ self._logger.info('Initializing proxy server')
52
+
53
+ def start(self):
54
+ self._logger.info(f"Starting proxy server with {self._adapter_type.value} adapter")
55
+
56
+ if self._adapter_type == AdapterType.SERIAL:
57
+ # If adapter type is serial, create the adapter directly
58
+ self._master_adapter = SerialPort(self._address, baudrate=self._baudrate)
59
+ elif self._adapter_type == AdapterType.IP:
60
+ # Otherwise, create a server to get IP clients
61
+ server = IPServer(port=self._port, transport='TCP', address=self._address, max_clients=1, stop_condition=None)
62
+ server.open()
63
+
64
+
65
+ # If the adapter type is IP, use the external while loop to get clients
66
+ while True:
67
+ self._master_adapter = server.get_client()
68
+ self._logger.info(f'Client connected : {self._master_adapter._address}:{self._master_adapter._port}')
69
+
70
+ while True:
71
+ try:
72
+ call_raw = self._master_adapter.read()
73
+ except AdapterDisconnected:
74
+ self._logger.info('Client disconnected')
75
+ break
76
+
77
+ api_call = parse(call_raw)
78
+
79
+ self._logger.debug(f'Received {type(api_call)}')
80
+
81
+ output = self.manage_call(api_call)
82
+
83
+ self._master_adapter.write(output.encode())
84
+
85
+ if self._adapter_type == AdapterType.IP:
86
+ if not self._master_adapter.read_thread_alive():
87
+ break
88
+
89
+ # Loop only if we need to get a new client
90
+ if self._adapter_type != AdapterType.IP:
91
+ break
92
+
93
+ def manage_call(self, c : APICall) -> APICall:
94
+ output = None
95
+ # IP Specific
96
+ if isinstance(c, IPInstanciate):
97
+ self._adapter = IP(
98
+ address=c.address,
99
+ port=c.port)
100
+ output = ReturnStatus(True)
101
+ # Serial specific
102
+ if isinstance(c, SerialPortInstanciate):
103
+ self._adapter = SerialPort(
104
+ port=c.port,
105
+ baudrate=c.baudrate
106
+ )
107
+ # Adapter
108
+ elif isinstance(c, AdapterOpen):
109
+ if self._adapter is None:
110
+ output = ReturnStatus(False, 'Cannot open uninstanciated adapter')
111
+ self._adapter.open()
112
+ output = ReturnStatus(True)
113
+ elif isinstance(c, AdapterClose):
114
+ if self._adapter is None:
115
+ output = ReturnStatus(False, 'Cannot close uninstanciated adapter')
116
+ else:
117
+ self._adapter.close()
118
+ output = ReturnStatus(True)
119
+ elif isinstance(c, AdapterWrite):
120
+ if self._adapter is None:
121
+ output = ReturnStatus(False, 'Cannot write to uninstanciated adapter')
122
+ else:
123
+ self._adapter.write(c.data)
124
+ output = ReturnStatus(True)
125
+ elif isinstance(c, AdapterFlushRead):
126
+ self._adapter.flushRead()
127
+ output = ReturnStatus(True)
128
+ elif isinstance(c, AdapterRead):
129
+ # TODO : Implement return_metrics
130
+ data = self._adapter.read()
131
+ output = AdapterReadReturn(data=data)
132
+
133
+ return output
134
+
135
+ if __name__ == '__main__':
136
+ main()
@@ -0,0 +1,93 @@
1
+ # proxy_api.py
2
+ # Sébastien Deriaz
3
+ # 29.05.2024
4
+
5
+ import sys
6
+ from dataclasses import dataclass
7
+
8
+ from ..api.api import APICall, ACTION_ATTRIBUTE, register_api, APIItem
9
+
10
+ class ProxyException(Exception):
11
+ pass
12
+
13
+ # IP specific
14
+ @dataclass
15
+ class IPInstanciate(APICall):
16
+ action = 'ip_adapter_inst'
17
+ address : str
18
+ port : int
19
+ transport : str
20
+ buffer_size : int
21
+
22
+ @dataclass
23
+ class TimeoutAPI(APIItem):
24
+ name = 'timeout'
25
+ response : float
26
+ continuation : float
27
+ total : float
28
+ on_response : str
29
+ on_continuation : str
30
+ on_total : str
31
+
32
+ @dataclass
33
+ class StopConditionAPI(APIItem):
34
+ pass
35
+
36
+ @dataclass
37
+ class TerminationAPI(StopConditionAPI):
38
+ name = 'termination'
39
+ sequence : bytes
40
+
41
+ @dataclass
42
+ class LengthAPI(StopConditionAPI):
43
+ name = 'length'
44
+ length : int
45
+
46
+ # Serial specific
47
+ @dataclass
48
+ class SerialPortInstanciate(APICall):
49
+ action = 'serial_adapter_inst'
50
+ port : str
51
+ baudrate : int
52
+ timeout : TimeoutAPI
53
+ stop_condition : StopConditionAPI
54
+ rts_cts : bool
55
+
56
+ # Adapters common
57
+ @dataclass
58
+ class AdapterOpen(APICall):
59
+ action = 'adapter_open'
60
+
61
+ @dataclass
62
+ class AdapterClose(APICall):
63
+ action = 'adapter_close'
64
+
65
+ @dataclass
66
+ class AdapterWrite(APICall):
67
+ action = 'adapter_write'
68
+ data : bytes
69
+
70
+ @dataclass
71
+ class AdapterFlushRead(APICall):
72
+ action = 'adapter_flush_read'
73
+
74
+ @dataclass
75
+ class AdapterRead(APICall):
76
+ action = 'adapter_read'
77
+
78
+ @dataclass
79
+ class AdapterReadReturn(APICall):
80
+ action = 'adapter_read_return'
81
+ data : bytes
82
+ return_metrics : dict = None
83
+
84
+ @dataclass
85
+ class ReturnStatus(APICall):
86
+ action = 'return_status'
87
+ success : bool
88
+ error_message : str = ''
89
+
90
+ # Register apis
91
+ current_module = sys.modules[__name__]
92
+ API_CALLS_PER_ACTION = {getattr(obj, ACTION_ATTRIBUTE) : obj for obj in current_module.__dict__.values() if hasattr(obj, ACTION_ATTRIBUTE)}
93
+ register_api(API_CALLS_PER_ACTION)
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: syndesi
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Syndesi
5
5
  Author: Sebastien Deriaz
6
6
  Author-email: sebastien.deriaz1@gmail.com
@@ -18,12 +18,17 @@ syndesi/adapters/stop_conditions.py
18
18
  syndesi/adapters/timed_queue.py
19
19
  syndesi/adapters/timeout.py
20
20
  syndesi/adapters/visa.py
21
+ syndesi/api/__init__.py
22
+ syndesi/api/api.py
21
23
  syndesi/protocols/__init__.py
22
24
  syndesi/protocols/delimited.py
23
25
  syndesi/protocols/protocol.py
24
26
  syndesi/protocols/raw.py
25
27
  syndesi/protocols/scpi.py
26
28
  syndesi/protocols/sdp.py
29
+ syndesi/proxy/__init__.py
30
+ syndesi/proxy/proxy.py
31
+ syndesi/proxy/proxy_api.py
27
32
  syndesi/tools/__init__.py
28
33
  syndesi/tools/exceptions.py
29
34
  syndesi/tools/log.py
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes