xaal.lib 0.7.7__py3-none-any.whl → 0.7.8__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.
- xaal/lib/__init__.py +1 -1
- xaal/lib/aioengine.py +155 -131
- xaal/lib/aiohelpers.py +7 -7
- xaal/lib/aionetwork.py +24 -24
- xaal/lib/bindings.py +30 -32
- xaal/lib/cbor.py +15 -10
- xaal/lib/config.py +71 -42
- xaal/lib/core.py +82 -65
- xaal/lib/devices.py +139 -96
- xaal/lib/engine.py +61 -43
- xaal/lib/exceptions.py +24 -8
- xaal/lib/helpers.py +38 -27
- xaal/lib/messages.py +171 -149
- xaal/lib/network.py +23 -20
- xaal/lib/test.py +25 -27
- xaal/lib/tools.py +50 -36
- {xaal.lib-0.7.7.dist-info → xaal.lib-0.7.8.dist-info}/METADATA +2 -2
- xaal.lib-0.7.8.dist-info/RECORD +22 -0
- {xaal.lib-0.7.7.dist-info → xaal.lib-0.7.8.dist-info}/WHEEL +1 -1
- xaal.lib-0.7.7.dist-info/RECORD +0 -22
- {xaal.lib-0.7.7.dist-info → xaal.lib-0.7.8.dist-info}/top_level.txt +0 -0
xaal/lib/devices.py
CHANGED
|
@@ -18,127 +18,156 @@
|
|
|
18
18
|
# along with xAAL. If not, see <http://www.gnu.org/licenses/>.
|
|
19
19
|
#
|
|
20
20
|
|
|
21
|
+
import logging
|
|
22
|
+
import time
|
|
23
|
+
import typing
|
|
24
|
+
from typing import Any, Optional, Union, Callable, Awaitable
|
|
25
|
+
|
|
26
|
+
from tabulate import tabulate
|
|
21
27
|
|
|
22
|
-
from . import config
|
|
23
|
-
from . import tools
|
|
24
|
-
from . import bindings
|
|
28
|
+
from . import bindings, config, tools
|
|
25
29
|
from .exceptions import DeviceError
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
import
|
|
31
|
+
if typing.TYPE_CHECKING:
|
|
32
|
+
from .aioengine import AsyncEngine
|
|
33
|
+
from .engine import Engine
|
|
34
|
+
from .core import EngineMixin
|
|
35
|
+
|
|
36
|
+
# Funtion types with any arguments and return a dict or None (device methods)
|
|
37
|
+
MethodT = Union[Callable[..., Union[dict, None]], Callable[..., Awaitable[Union[dict, None]]]]
|
|
38
|
+
|
|
29
39
|
logger = logging.getLogger(__name__)
|
|
30
40
|
|
|
31
|
-
import time
|
|
32
41
|
|
|
33
42
|
class Attribute(object):
|
|
34
|
-
|
|
35
|
-
def __init__(self, name, dev=None, default=None):
|
|
43
|
+
def __init__(self, name, dev: Optional['Device'] = None, default: Any = None):
|
|
36
44
|
self.name = name
|
|
37
45
|
self.default = default
|
|
38
|
-
self.device = dev
|
|
46
|
+
self.device: Optional['Device'] = dev
|
|
39
47
|
self.__value = default
|
|
40
48
|
|
|
41
49
|
@property
|
|
42
|
-
def value(self):
|
|
50
|
+
def value(self) -> Any:
|
|
43
51
|
return self.__value
|
|
44
52
|
|
|
45
53
|
@value.setter
|
|
46
|
-
def value(self, value):
|
|
54
|
+
def value(self, value: Any):
|
|
47
55
|
if value != self.__value and self.device:
|
|
48
56
|
eng = self.device.engine
|
|
49
57
|
if eng:
|
|
50
58
|
eng.add_attributes_change(self)
|
|
51
|
-
logger.debug("Attr change %s %s=%s" % (self.device.address,self.name,value))
|
|
59
|
+
logger.debug("Attr change %s %s=%s" % (self.device.address, self.name, value))
|
|
52
60
|
self.__value = value
|
|
53
61
|
|
|
54
|
-
def __repr__(self):
|
|
62
|
+
def __repr__(self) -> str: # pragma: no cover
|
|
55
63
|
return f"<{self.__module__}.Attribute {self.name} at 0x{id(self):x}>"
|
|
56
64
|
|
|
57
65
|
|
|
58
66
|
class Attributes(list):
|
|
59
67
|
"""Devices owns a attributes list. This list also have dict-like access"""
|
|
60
68
|
|
|
61
|
-
def __getitem__(self,value):
|
|
62
|
-
if isinstance(value,int):
|
|
63
|
-
return list.__getitem__(self,value)
|
|
69
|
+
def __getitem__(self, value):
|
|
70
|
+
if isinstance(value, int):
|
|
71
|
+
return list.__getitem__(self, value)
|
|
64
72
|
for k in self:
|
|
65
|
-
if
|
|
73
|
+
if value == k.name:
|
|
66
74
|
return k.value
|
|
67
75
|
raise KeyError(value)
|
|
68
76
|
|
|
69
|
-
def __setitem__(self,name,value):
|
|
70
|
-
if isinstance(name,int):
|
|
71
|
-
return list.__setitem__(self,name,value)
|
|
77
|
+
def __setitem__(self, name, value):
|
|
78
|
+
if isinstance(name, int):
|
|
79
|
+
return list.__setitem__(self, name, value)
|
|
72
80
|
for k in self:
|
|
73
|
-
if
|
|
81
|
+
if name == k.name:
|
|
74
82
|
k.value = value
|
|
75
83
|
return
|
|
76
84
|
raise KeyError(name)
|
|
77
85
|
|
|
78
|
-
class Device(object):
|
|
79
|
-
|
|
80
|
-
__slots__ = ['__dev_type','__address','group_id',
|
|
81
|
-
'vendor_id','product_id','hw_id',
|
|
82
|
-
'__version','__url','schema','info',
|
|
83
|
-
'unsupported_attributes','unsupported_methods','unsupported_notifications',
|
|
84
|
-
'alive_period','next_alive',
|
|
85
|
-
'__attributes','methods','engine']
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
class Device(object):
|
|
88
|
+
__slots__ = [
|
|
89
|
+
'__dev_type',
|
|
90
|
+
'__address',
|
|
91
|
+
'group_id',
|
|
92
|
+
'vendor_id',
|
|
93
|
+
'product_id',
|
|
94
|
+
'hw_id',
|
|
95
|
+
'__version',
|
|
96
|
+
'__url',
|
|
97
|
+
'schema',
|
|
98
|
+
'info',
|
|
99
|
+
'unsupported_attributes',
|
|
100
|
+
'unsupported_methods',
|
|
101
|
+
'unsupported_notifications',
|
|
102
|
+
'alive_period',
|
|
103
|
+
'next_alive',
|
|
104
|
+
'__attributes',
|
|
105
|
+
'methods',
|
|
106
|
+
'engine',
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
def __init__(
|
|
110
|
+
self,
|
|
111
|
+
dev_type: str,
|
|
112
|
+
addr: Optional[bindings.UUID] = None,
|
|
113
|
+
engine: Union['AsyncEngine', 'Engine', 'EngineMixin', None] = None,
|
|
114
|
+
):
|
|
88
115
|
# xAAL internal attributes for a device
|
|
89
|
-
self.dev_type = dev_type
|
|
90
|
-
self.address = addr
|
|
91
|
-
self.group_id = None
|
|
92
|
-
self.vendor_id = None
|
|
93
|
-
self.product_id = None
|
|
94
|
-
self.hw_id = None
|
|
95
|
-
self.
|
|
96
|
-
self.
|
|
97
|
-
self.schema = None
|
|
98
|
-
self.info = None
|
|
116
|
+
self.dev_type = dev_type # xaal dev_type
|
|
117
|
+
self.address = addr # xaal addr
|
|
118
|
+
self.group_id: Optional[bindings.UUID] = None # group devices
|
|
119
|
+
self.vendor_id: Optional[str] = None # vendor ID ie : ACME
|
|
120
|
+
self.product_id: Optional[str] = None # product ID
|
|
121
|
+
self.hw_id: Optional[str] = None # hardware info
|
|
122
|
+
self.version = None # product release
|
|
123
|
+
self.url = None # product URL
|
|
124
|
+
self.schema: Optional[str] = None # schema URL
|
|
125
|
+
self.info: Optional[str] = None # additionnal info
|
|
99
126
|
|
|
100
127
|
# Unsupported stuffs
|
|
101
128
|
self.unsupported_attributes = []
|
|
102
129
|
self.unsupported_methods = []
|
|
103
130
|
self.unsupported_notifications = []
|
|
104
131
|
# Alive management
|
|
105
|
-
self.alive_period = config.alive_timer
|
|
132
|
+
self.alive_period = config.alive_timer # time in sec between two alive
|
|
106
133
|
self.next_alive = 0
|
|
107
134
|
# Default attributes & methods
|
|
108
135
|
self.__attributes = Attributes()
|
|
109
|
-
self.methods = {
|
|
110
|
-
|
|
136
|
+
self.methods: dict[str, MethodT] = {
|
|
137
|
+
'get_attributes': self._get_attributes,
|
|
138
|
+
'get_description': self._get_description,
|
|
139
|
+
}
|
|
111
140
|
self.engine = engine
|
|
112
141
|
|
|
113
142
|
@property
|
|
114
|
-
def dev_type(self):
|
|
143
|
+
def dev_type(self) -> str:
|
|
115
144
|
return self.__dev_type
|
|
116
145
|
|
|
117
146
|
@dev_type.setter
|
|
118
|
-
def dev_type(self, value):
|
|
147
|
+
def dev_type(self, value: str):
|
|
119
148
|
if not tools.is_valid_dev_type(value):
|
|
120
149
|
raise DeviceError(f"The dev_type {value} is not valid")
|
|
121
150
|
self.__dev_type = value
|
|
122
151
|
|
|
123
152
|
@property
|
|
124
|
-
def version(self):
|
|
153
|
+
def version(self) -> Optional[str]:
|
|
125
154
|
return self.__version
|
|
126
155
|
|
|
127
156
|
@version.setter
|
|
128
|
-
def version(self, value):
|
|
129
|
-
#
|
|
157
|
+
def version(self, value: Any):
|
|
158
|
+
# version must be a string
|
|
130
159
|
if value:
|
|
131
160
|
self.__version = "%s" % value
|
|
132
161
|
else:
|
|
133
162
|
self.__version = None
|
|
134
163
|
|
|
135
164
|
@property
|
|
136
|
-
def address(self):
|
|
165
|
+
def address(self) -> Optional[bindings.UUID]:
|
|
137
166
|
return self.__address
|
|
138
167
|
|
|
139
168
|
@address.setter
|
|
140
|
-
def address(self, value):
|
|
141
|
-
if value
|
|
169
|
+
def address(self, value: Optional[bindings.UUID]):
|
|
170
|
+
if value is None:
|
|
142
171
|
self.__address = None
|
|
143
172
|
return
|
|
144
173
|
if not tools.is_valid_address(value):
|
|
@@ -146,61 +175,65 @@ class Device(object):
|
|
|
146
175
|
self.__address = value
|
|
147
176
|
|
|
148
177
|
@property
|
|
149
|
-
def url(self):
|
|
178
|
+
def url(self) -> Optional[bindings.URL]:
|
|
150
179
|
return self.__url
|
|
151
180
|
|
|
152
181
|
@url.setter
|
|
153
|
-
def url(self,value):
|
|
154
|
-
if value
|
|
182
|
+
def url(self, value: Optional[str]):
|
|
183
|
+
if value is None:
|
|
155
184
|
self.__url = None
|
|
156
185
|
else:
|
|
157
186
|
self.__url = bindings.URL(value)
|
|
158
187
|
|
|
159
188
|
# attributes
|
|
160
|
-
def new_attribute(self,name,default=None):
|
|
161
|
-
attr = Attribute(name,self,default)
|
|
189
|
+
def new_attribute(self, name: str, default: Any = None):
|
|
190
|
+
attr = Attribute(name, self, default)
|
|
162
191
|
self.add_attribute(attr)
|
|
163
192
|
return attr
|
|
164
193
|
|
|
165
|
-
def add_attribute(self, attr):
|
|
194
|
+
def add_attribute(self, attr: Attribute):
|
|
166
195
|
if attr:
|
|
167
196
|
self.__attributes.append(attr)
|
|
168
197
|
attr.device = self
|
|
169
198
|
|
|
170
|
-
def del_attribute(self,attr):
|
|
199
|
+
def del_attribute(self, attr: Attribute):
|
|
171
200
|
if attr:
|
|
172
201
|
attr.device = None
|
|
173
202
|
self.__attributes.remove(attr)
|
|
174
203
|
|
|
175
|
-
def get_attribute(self,name):
|
|
204
|
+
def get_attribute(self, name: str) -> Optional[Attribute]:
|
|
176
205
|
for attr in self.__attributes:
|
|
177
206
|
if attr.name == name:
|
|
178
207
|
return attr
|
|
179
208
|
return None
|
|
180
209
|
|
|
181
210
|
@property
|
|
182
|
-
def attributes(self):
|
|
211
|
+
def attributes(self) -> Attributes:
|
|
183
212
|
return self.__attributes
|
|
184
213
|
|
|
185
214
|
@attributes.setter
|
|
186
|
-
def attributes(self,values):
|
|
187
|
-
if isinstance(values,Attributes):
|
|
215
|
+
def attributes(self, values: Attributes):
|
|
216
|
+
if isinstance(values, Attributes):
|
|
188
217
|
self.__attributes = values
|
|
189
218
|
else:
|
|
190
219
|
raise DeviceError("Invalid attributes list, use class Attributes)")
|
|
191
220
|
|
|
192
|
-
def add_method(self,name,func):
|
|
193
|
-
self.methods.update({name:func})
|
|
221
|
+
def add_method(self, name: str, func: MethodT):
|
|
222
|
+
self.methods.update({name: func})
|
|
223
|
+
|
|
224
|
+
def del_method(self, name: str):
|
|
225
|
+
if name in self.methods:
|
|
226
|
+
del self.methods[name]
|
|
194
227
|
|
|
195
|
-
def get_methods(self):
|
|
228
|
+
def get_methods(self) -> dict[str, MethodT]:
|
|
196
229
|
return self.methods
|
|
197
230
|
|
|
198
231
|
def update_alive(self):
|
|
199
|
-
"""
|
|
232
|
+
"""update the alive timimg"""
|
|
200
233
|
self.next_alive = time.time() + self.alive_period
|
|
201
234
|
|
|
202
|
-
def get_timeout(self):
|
|
203
|
-
"""
|
|
235
|
+
def get_timeout(self) -> int:
|
|
236
|
+
"""return Alive timeout used for isAlive msg"""
|
|
204
237
|
return 2 * self.alive_period
|
|
205
238
|
|
|
206
239
|
#####################################################
|
|
@@ -210,46 +243,56 @@ class Device(object):
|
|
|
210
243
|
print("= Device: %s" % self)
|
|
211
244
|
# info & description
|
|
212
245
|
r = []
|
|
213
|
-
r.append(['dev_type',self.dev_type])
|
|
214
|
-
r.append(['address',self.address])
|
|
215
|
-
for k,v in self._get_description().items():
|
|
216
|
-
r.append([k,v])
|
|
217
|
-
print(tabulate(r,tablefmt=
|
|
246
|
+
r.append(['dev_type', self.dev_type])
|
|
247
|
+
r.append(['address', self.address])
|
|
248
|
+
for k, v in self._get_description().items():
|
|
249
|
+
r.append([k, v])
|
|
250
|
+
print(tabulate(r, tablefmt='fancy_grid'))
|
|
218
251
|
|
|
219
252
|
# attributes
|
|
220
253
|
if len(self._get_attributes()) > 0:
|
|
221
254
|
r = []
|
|
222
|
-
for k,v in self._get_attributes().items():
|
|
223
|
-
r.append([k,str(v)])
|
|
224
|
-
print(tabulate(r,tablefmt=
|
|
255
|
+
for k, v in self._get_attributes().items():
|
|
256
|
+
r.append([k, str(v)])
|
|
257
|
+
print(tabulate(r, tablefmt='fancy_grid'))
|
|
225
258
|
|
|
226
259
|
# methods
|
|
227
260
|
if len(self.methods) > 0:
|
|
228
261
|
r = []
|
|
229
|
-
for k,v in self.methods.items():
|
|
230
|
-
r.append([k,v.__name__])
|
|
231
|
-
print(tabulate(r,tablefmt=
|
|
232
|
-
|
|
262
|
+
for k, v in self.methods.items():
|
|
263
|
+
r.append([k, v.__name__])
|
|
264
|
+
print(tabulate(r, tablefmt='fancy_grid'))
|
|
233
265
|
|
|
234
|
-
def __repr__(self):
|
|
266
|
+
def __repr__(self) -> str:
|
|
235
267
|
return f"<xaal.Device {id(self):x} {self.address} {self.dev_type}>"
|
|
236
268
|
|
|
237
269
|
#####################################################
|
|
238
270
|
# default public methods
|
|
239
271
|
#####################################################
|
|
240
|
-
def _get_description(self):
|
|
272
|
+
def _get_description(self) -> dict:
|
|
241
273
|
result = {}
|
|
242
|
-
if self.vendor_id:
|
|
243
|
-
|
|
244
|
-
if self.
|
|
245
|
-
|
|
246
|
-
if self.
|
|
247
|
-
|
|
248
|
-
if self.
|
|
249
|
-
|
|
250
|
-
if self.
|
|
251
|
-
|
|
252
|
-
if self.
|
|
274
|
+
if self.vendor_id:
|
|
275
|
+
result['vendor_id'] = self.vendor_id
|
|
276
|
+
if self.product_id:
|
|
277
|
+
result['product_id'] = self.product_id
|
|
278
|
+
if self.version:
|
|
279
|
+
result['version'] = self.version
|
|
280
|
+
if self.url:
|
|
281
|
+
result['url'] = self.url
|
|
282
|
+
if self.schema:
|
|
283
|
+
result['schema'] = self.schema
|
|
284
|
+
if self.info:
|
|
285
|
+
result['info'] = self.info
|
|
286
|
+
if self.hw_id:
|
|
287
|
+
result['hw_id'] = self.hw_id
|
|
288
|
+
if self.group_id:
|
|
289
|
+
result['group_id'] = self.group_id
|
|
290
|
+
if self.unsupported_methods:
|
|
291
|
+
result['unsupported_methods'] = self.unsupported_methods
|
|
292
|
+
if self.unsupported_notifications:
|
|
293
|
+
result['unsupported_notifications'] = self.unsupported_notifications
|
|
294
|
+
if self.unsupported_attributes:
|
|
295
|
+
result['unsupported_attributes'] = self.unsupported_attributes
|
|
253
296
|
return result
|
|
254
297
|
|
|
255
298
|
def _get_attributes(self, _attributes=None):
|
|
@@ -279,7 +322,7 @@ class Device(object):
|
|
|
279
322
|
result.update({attr.name: attr.value})
|
|
280
323
|
return result
|
|
281
324
|
|
|
282
|
-
def send_notification(self,notification,body={}):
|
|
283
|
-
"""
|
|
325
|
+
def send_notification(self, notification: str, body: dict = {}):
|
|
326
|
+
"""queue an notification, this is just a method helper"""
|
|
284
327
|
if self.engine:
|
|
285
|
-
self.engine.send_notification(self,notification,body)
|
|
328
|
+
self.engine.send_notification(self, notification, body)
|
xaal/lib/engine.py
CHANGED
|
@@ -18,33 +18,44 @@
|
|
|
18
18
|
# along with xAAL. If not, see <http://www.gnu.org/licenses/>.
|
|
19
19
|
#
|
|
20
20
|
|
|
21
|
-
from . import core
|
|
22
|
-
from .network import NetworkConnector
|
|
23
|
-
from .exceptions import *
|
|
24
|
-
from . import config
|
|
25
|
-
|
|
26
|
-
import time
|
|
27
21
|
import collections
|
|
22
|
+
import logging
|
|
23
|
+
import time
|
|
24
|
+
import typing
|
|
28
25
|
from enum import Enum
|
|
26
|
+
from typing import Optional
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
from .config import config
|
|
31
|
+
from . import core
|
|
32
|
+
from .exceptions import CallbackError, MessageParserError, XAALError
|
|
33
|
+
from .network import NetworkConnector
|
|
34
|
+
|
|
35
|
+
if typing.TYPE_CHECKING:
|
|
36
|
+
from .devices import Device
|
|
37
|
+
from .messages import Message
|
|
38
|
+
|
|
29
39
|
|
|
30
|
-
import logging
|
|
31
40
|
logger = logging.getLogger(__name__)
|
|
32
41
|
|
|
33
|
-
class EngineState(Enum):
|
|
34
|
-
started = 1
|
|
35
|
-
running = 2
|
|
36
|
-
halted = 3
|
|
37
42
|
|
|
38
|
-
class
|
|
43
|
+
class EngineState(Enum):
|
|
44
|
+
started = 1
|
|
45
|
+
running = 2
|
|
46
|
+
halted = 3
|
|
39
47
|
|
|
40
|
-
__slots__ = ['__last_timer','__txFifo','state','network']
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
class Engine(core.EngineMixin):
|
|
50
|
+
__slots__ = ['__last_timer', '__txFifo', 'state', 'network']
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
self.
|
|
52
|
+
def __init__(
|
|
53
|
+
self, address: str = config.address, port: int = config.port, hops: int = config.hops, key: bytes = config.key
|
|
54
|
+
):
|
|
55
|
+
core.EngineMixin.__init__(self, address, port, hops, key)
|
|
47
56
|
|
|
57
|
+
self.__last_timer = 0 # last timer check
|
|
58
|
+
self.__txFifo = collections.deque() # tx msg fifo
|
|
48
59
|
# message receive workflow
|
|
49
60
|
self.subscribe(self.handle_request)
|
|
50
61
|
# ready to go
|
|
@@ -56,16 +67,16 @@ class Engine(core.EngineMixin):
|
|
|
56
67
|
# xAAL messages Tx handling
|
|
57
68
|
#####################################################
|
|
58
69
|
# Fifo for msg to send
|
|
59
|
-
def queue_msg(self, msg):
|
|
70
|
+
def queue_msg(self, msg: bytes):
|
|
60
71
|
"""queue an encoded / cyphered message"""
|
|
61
72
|
self.__txFifo.append(msg)
|
|
62
73
|
|
|
63
|
-
def send_msg(self, msg):
|
|
74
|
+
def send_msg(self, msg: bytes):
|
|
64
75
|
"""Send an encoded message to the bus, use queue_msg instead"""
|
|
65
76
|
self.network.send(msg)
|
|
66
77
|
|
|
67
78
|
def process_tx_msg(self):
|
|
68
|
-
"""
|
|
79
|
+
"""Process (send) message in tx queue called from the loop()"""
|
|
69
80
|
cnt = 0
|
|
70
81
|
while self.__txFifo:
|
|
71
82
|
temp = self.__txFifo.popleft()
|
|
@@ -79,13 +90,13 @@ class Engine(core.EngineMixin):
|
|
|
79
90
|
#####################################################
|
|
80
91
|
# xAAL messages subscribers
|
|
81
92
|
#####################################################
|
|
82
|
-
def receive_msg(self):
|
|
93
|
+
def receive_msg(self) -> Optional['Message']:
|
|
83
94
|
"""return new received message or None"""
|
|
84
95
|
result = None
|
|
85
96
|
data = self.network.get_data()
|
|
86
97
|
if data:
|
|
87
98
|
try:
|
|
88
|
-
msg = self.msg_factory.decode_msg(data,self.msg_filter)
|
|
99
|
+
msg = self.msg_factory.decode_msg(data, self.msg_filter)
|
|
89
100
|
except MessageParserError as e:
|
|
90
101
|
logger.warning(e)
|
|
91
102
|
msg = None
|
|
@@ -99,35 +110,37 @@ class Engine(core.EngineMixin):
|
|
|
99
110
|
for func in self.subscribers:
|
|
100
111
|
func(msg)
|
|
101
112
|
self.process_attributes_change()
|
|
102
|
-
|
|
103
|
-
def handle_request(self, msg):
|
|
113
|
+
|
|
114
|
+
def handle_request(self, msg: 'Message'):
|
|
104
115
|
"""
|
|
105
116
|
Filter msg for devices according default xAAL API then process the
|
|
106
117
|
request for each targets identied in the engine
|
|
107
118
|
"""
|
|
108
119
|
if not msg.is_request():
|
|
109
|
-
return
|
|
110
|
-
|
|
120
|
+
return
|
|
121
|
+
|
|
111
122
|
targets = core.filter_msg_for_devices(msg, self.devices)
|
|
112
123
|
for target in targets:
|
|
113
|
-
if msg.
|
|
124
|
+
if msg.is_request_isalive():
|
|
114
125
|
self.send_alive(target)
|
|
115
126
|
else:
|
|
116
127
|
self.handle_action_request(msg, target)
|
|
117
128
|
|
|
118
|
-
def handle_action_request(self, msg, target):
|
|
129
|
+
def handle_action_request(self, msg: 'Message', target: 'Device'):
|
|
119
130
|
"""
|
|
120
131
|
Run method (xAAL exposed method) on device:
|
|
121
|
-
- None is returned if device method do not return anything
|
|
122
132
|
- result is returned if device method gives a response
|
|
123
133
|
- Errors are raised if an error occured:
|
|
124
134
|
* Internal error
|
|
125
135
|
* error returned on the xAAL bus
|
|
126
136
|
"""
|
|
137
|
+
if msg.action is None:
|
|
138
|
+
return # should not happen, but pyright need this check
|
|
139
|
+
|
|
127
140
|
try:
|
|
128
141
|
result = run_action(msg, target)
|
|
129
|
-
if result
|
|
130
|
-
self.send_reply(dev=target,targets=[msg.source],action=msg.action,body=result)
|
|
142
|
+
if result:
|
|
143
|
+
self.send_reply(dev=target, targets=[msg.source], action=msg.action, body=result)
|
|
131
144
|
except CallbackError as e:
|
|
132
145
|
self.send_error(target, e.code, e.description)
|
|
133
146
|
except XAALError as e:
|
|
@@ -139,12 +152,13 @@ class Engine(core.EngineMixin):
|
|
|
139
152
|
def process_timers(self):
|
|
140
153
|
"""Process all timers to find out which ones should be run"""
|
|
141
154
|
expire_list = []
|
|
142
|
-
|
|
143
|
-
if len(self.timers)!=0
|
|
155
|
+
|
|
156
|
+
if len(self.timers) != 0:
|
|
144
157
|
now = time.time()
|
|
145
158
|
# little hack to avoid to check timer to often.
|
|
146
159
|
# w/ this enable timer precision is bad, but far enougth
|
|
147
|
-
if (now - self.__last_timer) < 0.4:
|
|
160
|
+
if (now - self.__last_timer) < 0.4:
|
|
161
|
+
return
|
|
148
162
|
|
|
149
163
|
for t in self.timers:
|
|
150
164
|
if t.deadline < now:
|
|
@@ -153,14 +167,14 @@ class Engine(core.EngineMixin):
|
|
|
153
167
|
except CallbackError as e:
|
|
154
168
|
logger.error(e.description)
|
|
155
169
|
if t.counter != -1:
|
|
156
|
-
t.counter-= 1
|
|
170
|
+
t.counter -= 1
|
|
157
171
|
if t.counter == 0:
|
|
158
172
|
expire_list.append(t)
|
|
159
173
|
t.deadline = now + t.period
|
|
160
174
|
# delete expired timers
|
|
161
175
|
for t in expire_list:
|
|
162
176
|
self.remove_timer(t)
|
|
163
|
-
|
|
177
|
+
|
|
164
178
|
self.__last_timer = now
|
|
165
179
|
|
|
166
180
|
#####################################################
|
|
@@ -187,7 +201,7 @@ class Engine(core.EngineMixin):
|
|
|
187
201
|
|
|
188
202
|
def start(self):
|
|
189
203
|
"""Start the core engine: send queue alive msg"""
|
|
190
|
-
if self.state in [EngineState.started,EngineState.running]:
|
|
204
|
+
if self.state in [EngineState.started, EngineState.running]:
|
|
191
205
|
return
|
|
192
206
|
self.network.connect()
|
|
193
207
|
for dev in self.devices:
|
|
@@ -206,13 +220,14 @@ class Engine(core.EngineMixin):
|
|
|
206
220
|
while self.state == EngineState.running:
|
|
207
221
|
self.loop()
|
|
208
222
|
|
|
209
|
-
def is_running(self):
|
|
223
|
+
def is_running(self) -> bool:
|
|
210
224
|
if self.state == EngineState.running:
|
|
211
225
|
return True
|
|
212
226
|
return False
|
|
213
227
|
|
|
214
|
-
|
|
215
|
-
|
|
228
|
+
|
|
229
|
+
def run_action(msg: 'Message', device: 'Device') -> Optional[dict]:
|
|
230
|
+
"""
|
|
216
231
|
Extract an action & launch it
|
|
217
232
|
Return:
|
|
218
233
|
- action result
|
|
@@ -220,12 +235,15 @@ def run_action(msg,device):
|
|
|
220
235
|
|
|
221
236
|
Note: If an exception raised, it's logged, and raise an XAALError.
|
|
222
237
|
"""
|
|
223
|
-
method,params = core.search_action(msg,device)
|
|
238
|
+
method, params = core.search_action(msg, device)
|
|
224
239
|
result = None
|
|
225
240
|
try:
|
|
226
241
|
result = method(**params)
|
|
227
242
|
except Exception as e:
|
|
228
243
|
logger.error(e)
|
|
229
|
-
raise XAALError("Error in method:%s params:%s" % (msg.action,params))
|
|
244
|
+
raise XAALError("Error in method:%s params:%s" % (msg.action, params))
|
|
245
|
+
# Here result should be None or a dict, and we need to enforce that. This will cause issue
|
|
246
|
+
# in send_reply otherwise.
|
|
247
|
+
if result is not None and not isinstance(result, dict):
|
|
248
|
+
raise XAALError("Method %s should return a dict or None" % msg.action)
|
|
230
249
|
return result
|
|
231
|
-
|
xaal/lib/exceptions.py
CHANGED
|
@@ -1,20 +1,36 @@
|
|
|
1
|
-
|
|
2
1
|
# devices.py
|
|
3
|
-
class DeviceError(Exception):
|
|
2
|
+
class DeviceError(Exception):
|
|
3
|
+
pass
|
|
4
4
|
|
|
5
5
|
# core.py
|
|
6
|
-
class EngineError(Exception):
|
|
7
|
-
|
|
6
|
+
class EngineError(Exception):
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class XAALError(Exception):
|
|
10
|
+
pass
|
|
11
|
+
|
|
8
12
|
class CallbackError(Exception):
|
|
9
13
|
def __init__(self, code, desc):
|
|
10
14
|
self.code = code
|
|
11
15
|
self.description = desc
|
|
12
16
|
|
|
13
17
|
# messages.py
|
|
14
|
-
class MessageParserError(Exception):
|
|
15
|
-
|
|
18
|
+
class MessageParserError(Exception):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
class MessageError(Exception):
|
|
22
|
+
pass
|
|
16
23
|
|
|
17
24
|
# binding.py
|
|
18
|
-
class UUIDError(Exception):
|
|
25
|
+
class UUIDError(Exception):
|
|
26
|
+
pass
|
|
19
27
|
|
|
20
|
-
__all__ = [
|
|
28
|
+
__all__ = [
|
|
29
|
+
'DeviceError',
|
|
30
|
+
'EngineError',
|
|
31
|
+
'XAALError',
|
|
32
|
+
'CallbackError',
|
|
33
|
+
'MessageParserError',
|
|
34
|
+
'MessageError',
|
|
35
|
+
'UUIDError',
|
|
36
|
+
]
|