xaal.lib 0.7.7__py3-none-any.whl → 0.7.9__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 +3 -5
- 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 +85 -43
- xaal/lib/core.py +82 -65
- xaal/lib/devices.py +139 -96
- xaal/lib/engine.py +59 -43
- xaal/lib/exceptions.py +24 -8
- xaal/lib/helpers.py +38 -27
- xaal/lib/messages.py +174 -151
- 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.9.dist-info}/METADATA +2 -2
- xaal.lib-0.7.9.dist-info/RECORD +22 -0
- {xaal.lib-0.7.7.dist-info → xaal.lib-0.7.9.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.9.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, Dict
|
|
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,42 @@
|
|
|
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
|
+
from .config import config
|
|
29
|
+
from . import core
|
|
30
|
+
from .exceptions import CallbackError, MessageParserError, XAALError
|
|
31
|
+
from .network import NetworkConnector
|
|
32
|
+
|
|
33
|
+
if typing.TYPE_CHECKING:
|
|
34
|
+
from .devices import Device
|
|
35
|
+
from .messages import Message
|
|
36
|
+
|
|
29
37
|
|
|
30
|
-
import logging
|
|
31
38
|
logger = logging.getLogger(__name__)
|
|
32
39
|
|
|
33
|
-
class EngineState(Enum):
|
|
34
|
-
started = 1
|
|
35
|
-
running = 2
|
|
36
|
-
halted = 3
|
|
37
40
|
|
|
38
|
-
class
|
|
41
|
+
class EngineState(Enum):
|
|
42
|
+
started = 1
|
|
43
|
+
running = 2
|
|
44
|
+
halted = 3
|
|
39
45
|
|
|
40
|
-
__slots__ = ['__last_timer','__txFifo','state','network']
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
class Engine(core.EngineMixin):
|
|
48
|
+
__slots__ = ['__last_timer', '__txFifo', 'state', 'network']
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
self.
|
|
50
|
+
def __init__(
|
|
51
|
+
self, address: str = config.address, port: int = config.port, hops: int = config.hops, key: bytes = config.key
|
|
52
|
+
):
|
|
53
|
+
core.EngineMixin.__init__(self, address, port, hops, key)
|
|
47
54
|
|
|
55
|
+
self.__last_timer = 0 # last timer check
|
|
56
|
+
self.__txFifo = collections.deque() # tx msg fifo
|
|
48
57
|
# message receive workflow
|
|
49
58
|
self.subscribe(self.handle_request)
|
|
50
59
|
# ready to go
|
|
@@ -56,16 +65,16 @@ class Engine(core.EngineMixin):
|
|
|
56
65
|
# xAAL messages Tx handling
|
|
57
66
|
#####################################################
|
|
58
67
|
# Fifo for msg to send
|
|
59
|
-
def queue_msg(self, msg):
|
|
68
|
+
def queue_msg(self, msg: bytes):
|
|
60
69
|
"""queue an encoded / cyphered message"""
|
|
61
70
|
self.__txFifo.append(msg)
|
|
62
71
|
|
|
63
|
-
def send_msg(self, msg):
|
|
72
|
+
def send_msg(self, msg: bytes):
|
|
64
73
|
"""Send an encoded message to the bus, use queue_msg instead"""
|
|
65
74
|
self.network.send(msg)
|
|
66
75
|
|
|
67
76
|
def process_tx_msg(self):
|
|
68
|
-
"""
|
|
77
|
+
"""Process (send) message in tx queue called from the loop()"""
|
|
69
78
|
cnt = 0
|
|
70
79
|
while self.__txFifo:
|
|
71
80
|
temp = self.__txFifo.popleft()
|
|
@@ -79,13 +88,13 @@ class Engine(core.EngineMixin):
|
|
|
79
88
|
#####################################################
|
|
80
89
|
# xAAL messages subscribers
|
|
81
90
|
#####################################################
|
|
82
|
-
def receive_msg(self):
|
|
91
|
+
def receive_msg(self) -> Optional['Message']:
|
|
83
92
|
"""return new received message or None"""
|
|
84
93
|
result = None
|
|
85
94
|
data = self.network.get_data()
|
|
86
95
|
if data:
|
|
87
96
|
try:
|
|
88
|
-
msg = self.msg_factory.decode_msg(data,self.msg_filter)
|
|
97
|
+
msg = self.msg_factory.decode_msg(data, self.msg_filter)
|
|
89
98
|
except MessageParserError as e:
|
|
90
99
|
logger.warning(e)
|
|
91
100
|
msg = None
|
|
@@ -99,35 +108,37 @@ class Engine(core.EngineMixin):
|
|
|
99
108
|
for func in self.subscribers:
|
|
100
109
|
func(msg)
|
|
101
110
|
self.process_attributes_change()
|
|
102
|
-
|
|
103
|
-
def handle_request(self, msg):
|
|
111
|
+
|
|
112
|
+
def handle_request(self, msg: 'Message'):
|
|
104
113
|
"""
|
|
105
114
|
Filter msg for devices according default xAAL API then process the
|
|
106
115
|
request for each targets identied in the engine
|
|
107
116
|
"""
|
|
108
117
|
if not msg.is_request():
|
|
109
|
-
return
|
|
110
|
-
|
|
118
|
+
return
|
|
119
|
+
|
|
111
120
|
targets = core.filter_msg_for_devices(msg, self.devices)
|
|
112
121
|
for target in targets:
|
|
113
|
-
if msg.
|
|
122
|
+
if msg.is_request_isalive():
|
|
114
123
|
self.send_alive(target)
|
|
115
124
|
else:
|
|
116
125
|
self.handle_action_request(msg, target)
|
|
117
126
|
|
|
118
|
-
def handle_action_request(self, msg, target):
|
|
127
|
+
def handle_action_request(self, msg: 'Message', target: 'Device'):
|
|
119
128
|
"""
|
|
120
129
|
Run method (xAAL exposed method) on device:
|
|
121
|
-
- None is returned if device method do not return anything
|
|
122
130
|
- result is returned if device method gives a response
|
|
123
131
|
- Errors are raised if an error occured:
|
|
124
132
|
* Internal error
|
|
125
133
|
* error returned on the xAAL bus
|
|
126
134
|
"""
|
|
135
|
+
if msg.action is None:
|
|
136
|
+
return # should not happen, but pyright need this check
|
|
137
|
+
|
|
127
138
|
try:
|
|
128
139
|
result = run_action(msg, target)
|
|
129
|
-
if result
|
|
130
|
-
self.send_reply(dev=target,targets=[msg.source],action=msg.action,body=result)
|
|
140
|
+
if result is not None:
|
|
141
|
+
self.send_reply(dev=target, targets=[msg.source], action=msg.action, body=result)
|
|
131
142
|
except CallbackError as e:
|
|
132
143
|
self.send_error(target, e.code, e.description)
|
|
133
144
|
except XAALError as e:
|
|
@@ -139,12 +150,13 @@ class Engine(core.EngineMixin):
|
|
|
139
150
|
def process_timers(self):
|
|
140
151
|
"""Process all timers to find out which ones should be run"""
|
|
141
152
|
expire_list = []
|
|
142
|
-
|
|
143
|
-
if len(self.timers)!=0
|
|
153
|
+
|
|
154
|
+
if len(self.timers) != 0:
|
|
144
155
|
now = time.time()
|
|
145
156
|
# little hack to avoid to check timer to often.
|
|
146
157
|
# w/ this enable timer precision is bad, but far enougth
|
|
147
|
-
if (now - self.__last_timer) < 0.4:
|
|
158
|
+
if (now - self.__last_timer) < 0.4:
|
|
159
|
+
return
|
|
148
160
|
|
|
149
161
|
for t in self.timers:
|
|
150
162
|
if t.deadline < now:
|
|
@@ -153,14 +165,14 @@ class Engine(core.EngineMixin):
|
|
|
153
165
|
except CallbackError as e:
|
|
154
166
|
logger.error(e.description)
|
|
155
167
|
if t.counter != -1:
|
|
156
|
-
t.counter-= 1
|
|
168
|
+
t.counter -= 1
|
|
157
169
|
if t.counter == 0:
|
|
158
170
|
expire_list.append(t)
|
|
159
171
|
t.deadline = now + t.period
|
|
160
172
|
# delete expired timers
|
|
161
173
|
for t in expire_list:
|
|
162
174
|
self.remove_timer(t)
|
|
163
|
-
|
|
175
|
+
|
|
164
176
|
self.__last_timer = now
|
|
165
177
|
|
|
166
178
|
#####################################################
|
|
@@ -187,7 +199,7 @@ class Engine(core.EngineMixin):
|
|
|
187
199
|
|
|
188
200
|
def start(self):
|
|
189
201
|
"""Start the core engine: send queue alive msg"""
|
|
190
|
-
if self.state in [EngineState.started,EngineState.running]:
|
|
202
|
+
if self.state in [EngineState.started, EngineState.running]:
|
|
191
203
|
return
|
|
192
204
|
self.network.connect()
|
|
193
205
|
for dev in self.devices:
|
|
@@ -206,13 +218,14 @@ class Engine(core.EngineMixin):
|
|
|
206
218
|
while self.state == EngineState.running:
|
|
207
219
|
self.loop()
|
|
208
220
|
|
|
209
|
-
def is_running(self):
|
|
221
|
+
def is_running(self) -> bool:
|
|
210
222
|
if self.state == EngineState.running:
|
|
211
223
|
return True
|
|
212
224
|
return False
|
|
213
225
|
|
|
214
|
-
|
|
215
|
-
|
|
226
|
+
|
|
227
|
+
def run_action(msg: 'Message', device: 'Device') -> Optional[dict]:
|
|
228
|
+
"""
|
|
216
229
|
Extract an action & launch it
|
|
217
230
|
Return:
|
|
218
231
|
- action result
|
|
@@ -220,12 +233,15 @@ def run_action(msg,device):
|
|
|
220
233
|
|
|
221
234
|
Note: If an exception raised, it's logged, and raise an XAALError.
|
|
222
235
|
"""
|
|
223
|
-
method,params = core.search_action(msg,device)
|
|
236
|
+
method, params = core.search_action(msg, device)
|
|
224
237
|
result = None
|
|
225
238
|
try:
|
|
226
239
|
result = method(**params)
|
|
227
240
|
except Exception as e:
|
|
228
241
|
logger.error(e)
|
|
229
|
-
raise XAALError("Error in method:%s params:%s" % (msg.action,params))
|
|
242
|
+
raise XAALError("Error in method:%s params:%s" % (msg.action, params))
|
|
243
|
+
# Here result should be None or a dict, and we need to enforce that. This will cause issue
|
|
244
|
+
# in send_reply otherwise.
|
|
245
|
+
if result is not None and not isinstance(result, dict):
|
|
246
|
+
raise XAALError("Method %s should return a dict or None" % msg.action)
|
|
230
247
|
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
|
+
]
|