urfp 0.2__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.
urfp/__init__.py ADDED
File without changes
urfp/_version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.2"
urfp/argtype.py ADDED
@@ -0,0 +1,114 @@
1
+ """ Argument type description class for use in tinypacket """
2
+
3
+ class URFPArgType:
4
+ """ This class is used to describe the data structure of one variable, function
5
+ return value, function arguments, or source content. It is especially needed when
6
+ receiving data from simple devices that cannot efficiently augment all data
7
+ with field names and type info, e.g. in URFPTinypacket """
8
+
9
+ # Binary representation as in Tinypacket
10
+ URFP_TYPE_INFO = 0x00
11
+
12
+ # Type byte
13
+ URFP_STRUCT_START = 0x40
14
+ URFP_ARRAY_START = 0x41 # followed by int32 array size (-1 or > 0)
15
+ URFP_ARRAY_END = 0x42
16
+ URFP_STRUCT_END = 0x43
17
+ URFP_BOOL = 0x44
18
+ URFP_STRING = 0x45
19
+ URFP_FLOAT = 0x46
20
+ URFP_DOUBLE = 0x47
21
+ URFP_INT = 0x48 # to be completed with ld2(wordlen) in LSB
22
+ URFP_INT8 = 0x48 # = URFP_INT | ld2(1) = URFP_INT
23
+ URFP_INT16 = 0x49 # = URFP_INT | ld2(2)
24
+ URFP_INT32 = 0x4A # = URFP_INT | ld2(4)
25
+ URFP_INT64 = 0x4B # = URFP_INT | ld2(8)
26
+ URFP_UNSIGNED = 0x4C # to be completed with ld2(wordlen) in LSB
27
+ URFP_UINT8 = 0x4C # = URFP_UNSIGNED | ld2(1) = URFP_UNSIGNED
28
+ URFP_UINT16 = 0x4D # = URFP_UNSIGNED | ld2(2)
29
+ URFP_UINT32 = 0x4E # = URFP_UNSIGNED | ld2(4)
30
+ URFP_UINT64 = 0x4F # = URFP_UNSIGNED | ld2(8)
31
+
32
+ # 0x50..0xFF reserved for custom types
33
+
34
+ # Type mode
35
+ URFP_ARG = 0x01 # Indicates that type information describes an argument not result
36
+ URFP_NAMED = 0x02 # Indicates that a name string follows type information
37
+
38
+ def __init__(self, name, length=0, type_byte=None, type_str=None):
39
+ self.name = name
40
+ self.length = length
41
+ if type_str:
42
+ if type_str == '{':
43
+ self.code = URFPArgType.URFP_STRUCT_START
44
+ if type_str[0] == '[':
45
+ self.code = URFPArgType.URFP_ARRAY_START
46
+ if length==0:
47
+ if len(type_str)>0:
48
+ self.length = int(type_str[1:])
49
+ else:
50
+ self.length = -1
51
+ if type_str == ']':
52
+ self.code = URFPArgType.URFP_ARRAY_END
53
+ if type_str == '}':
54
+ self.code = URFPArgType.URFP_STRUCT_END
55
+ if type_str == 'bool':
56
+ self.code = URFPArgType.URFP_BOOL
57
+ elif type_str == 'string':
58
+ self.code = URFPArgType.URFP_STRING
59
+ elif type_str == 'float':
60
+ self.code = URFPArgType.URFP_FLOAT
61
+ elif type_str == 'double':
62
+ self.code = URFPArgType.URFP_DOUBLE
63
+ elif type_str[0] == 'i':
64
+ self.code = URFPArgType.URFP_INT
65
+ elif type_str[0] == 'u':
66
+ self.code = URFPArgType.URFP_UNSIGNED
67
+
68
+ if self.code in (URFPArgType.URFP_INT, URFPArgType.URFP_UNSIGNED):
69
+ if type_str[1:] == '16':
70
+ self.code = self.code | 0x01
71
+ elif type_str[1:] == '32':
72
+ self.code = self.code | 0x02
73
+ elif type_str[1:] == '64':
74
+ self.code = self.code | 0x03
75
+
76
+ else: # no type_str
77
+ if type_byte is None:
78
+ raise TypeError('Either type_byte or type_str must be specified')
79
+
80
+ self.code = type_byte
81
+ if type_byte == 0x41 and length==0:
82
+ raise TypeError('A nonzero length must be given for arrays')
83
+
84
+ def __str__(self):
85
+ if self.code == URFPArgType.URFP_STRUCT_START:
86
+ t = self.name + ':' + '{'
87
+ elif self.code == URFPArgType.URFP_ARRAY_START:
88
+ if self.length != -1:
89
+ t = f'{self.name}:[{self.length}'
90
+ else:
91
+ t = f'{self.name}:['
92
+ elif self.code == URFPArgType.URFP_ARRAY_END:
93
+ t = ']'
94
+ elif self.code == URFPArgType.URFP_STRUCT_END:
95
+ t = '}'
96
+ elif self.code == URFPArgType.URFP_BOOL:
97
+ t = self.name + ':' + 'bool'
98
+ elif self.code == URFPArgType.URFP_STRING:
99
+ t = self.name + ':' + 'string'
100
+ elif URFPArgType.URFP_INT <= self.code < URFPArgType.URFP_INT+0x08: # some int
101
+ width = 8 << (self.code & 3)
102
+ if self.code < URFPArgType.URFP_UNSIGNED:
103
+ t = f'{self.name}:i{width}'
104
+ else: # URFP_UNSIGNED
105
+ t = f'{self.name}:u{width}'
106
+ elif self.code == URFPArgType.URFP_FLOAT:
107
+ t = f'{self.name}:float'
108
+ elif self.code == URFPArgType.URFP_DOUBLE:
109
+ t = f'{self.name}:double'
110
+ elif self.code == URFPArgType.URFP_TYPE_INFO:
111
+ t = f'{self.name}:<type info>'
112
+ else:
113
+ t = f'{self.name}:<unknown({int(self.code)})>'
114
+ return t
urfp/console.py ADDED
@@ -0,0 +1,111 @@
1
+ """ Peer for talking to URFP console server """
2
+
3
+ import re
4
+ import time
5
+ import logging
6
+ import serial
7
+
8
+ class URFPConsoleChatter:
9
+ """ This class provides methods to talk to a URFP Console server """
10
+
11
+ def __init__(self, device="/dev/ttyS0", logger=None):
12
+ """Constructor """
13
+ self._logger = (logger if logger else logging.getLogger(__name__))
14
+ self._serial = serial.Serial(device, 115200, timeout=1)
15
+ self._tag = 64 # ord() of most recently used tag
16
+ self._env = { }
17
+
18
+ def __exit__(self, type_, value, traceback):
19
+ self._serial.close()
20
+
21
+ def _send(self,q):
22
+ self._logger.debug('TX:%s', str(q))
23
+ for c in q:
24
+ self._serial.write([c])
25
+ time.sleep(0.01)
26
+ self._serial.write(b'\r')
27
+ time.sleep(0.01)
28
+
29
+ def _recv(self):
30
+ r = self._serial.readline()
31
+ if r:
32
+ self._logger.debug('RX:%s', str(r))
33
+ else:
34
+ self._logger.debug('-nothing-')
35
+ return r
36
+
37
+ ##==--
38
+
39
+ def _next_tag(self, curr_tag):
40
+ next_tag = curr_tag + 1
41
+ if next_tag > ord('Z'):
42
+ next_tag = ord('A')
43
+ return next_tag
44
+
45
+ def perform_request(self, code, *args):
46
+ """ This method performs the request. 'code' is the two-letter code without the tag.
47
+ The tag is chosen and added by this method as appropriate. The args should be a dict
48
+ of data to transmit with the request. They will be serialized in the order in which
49
+ they appear in the dict (starting with Python 3.7) and must match whatever the server
50
+ expects regarding order, type and size. The serializer is work in progress, see serialize()
51
+ """
52
+
53
+ tag = None
54
+ new_tag = self._next_tag(self._tag)
55
+ while not tag and new_tag != self._tag:
56
+ if new_tag not in self._env:
57
+ tag = new_tag # found an unused tag
58
+ else:
59
+ new_tag = self._next_tag(new_tag)
60
+
61
+ if not tag:
62
+ self._logger.info("Didn't find an unique new tag for request")
63
+ return None
64
+
65
+ self._env[tag] = { 'code':code, 'final': False, 'input': r'' }
66
+
67
+ # args is a dict. Only use the values
68
+ output = code + chr(tag) + ' ' + ' '.join(args[0].values())
69
+
70
+ # Transmit, with optional small delay between bytes (slow UART in device)
71
+
72
+ self._send(b'')
73
+ self._recv() # synced
74
+
75
+ self._send(bytes(output, encoding='ascii'))
76
+ self._recv() # ignore echo
77
+
78
+ # Receive. Todo: Check TAG
79
+
80
+ retval = None
81
+ final = r'\*' + code + '.'
82
+ line = self._recv()
83
+ while line:
84
+ line = line.decode('utf-8')
85
+ # Split line at whitespaces; keep strings enclosed with "" and remove the quotes
86
+ matches = re.findall(r'"(.*?)"|([^\s]+)', line)
87
+ words = [m[0] if m[0]!='' else m[1] for m in matches]
88
+ if re.match(final, words[0]):
89
+ return retval
90
+ # Return words in a list of lists
91
+ if len(words) > 0:
92
+ if retval is None:
93
+ retval = []
94
+ retval.append(words[1:-1])
95
+ line = self._recv()
96
+ return retval
97
+
98
+ def perform_list_request(self, cmd, listname, params):
99
+ """ retrieve array[l of (i32,string) pairs AKA (id,name) """
100
+ results:list[list[str,str]] = self.perform_request(cmd, params)
101
+ # Return as a JSON-like dictionary
102
+ json_results = {listname: [{"id": int(items[0]), "name": items[1]} for items in results]}
103
+ return json_results
104
+
105
+ def perform_help_request(self, elemtype, elemid):
106
+ """ Ask for details about element """
107
+ json_response = self.perform_request('h'+elemtype, {'id':str(elemid)})
108
+ help_dict = { 'desc': json_response['desc'], 'type': {}, 'args': {} }
109
+ for k in json_response:
110
+ if k != 'desc':
111
+ help_dict['type'][k] = json_response[k]