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 CHANGED
@@ -3,8 +3,8 @@
3
3
  # Load main class & modules.
4
4
 
5
5
 
6
+ from .config import config
6
7
  from . import tools
7
- from . import config
8
8
  from . import bindings
9
9
  from . import aiohelpers as helpers
10
10
 
xaal/lib/aioengine.py CHANGED
@@ -1,45 +1,80 @@
1
1
  import asyncio
2
-
3
- from . import core
4
- from . import config
5
- from . import tools
6
- from .messages import MessageParserError
7
- from .aionetwork import AsyncNetworkConnector
8
- from .exceptions import *
9
-
2
+ import logging
3
+ import signal
4
+ import sys
10
5
  import time
6
+ import typing
11
7
  from enum import Enum
8
+ from pprint import pprint
9
+ from typing import Any, Optional
10
+ from uuid import UUID
11
+
12
12
  import aioconsole
13
- import signal
14
- import sys
15
13
  from tabulate import tabulate
16
- from pprint import pprint
17
14
 
15
+ from .config import config
16
+ from . import core
17
+ from .aionetwork import AsyncNetworkConnector
18
+ from .exceptions import CallbackError, XAALError
19
+ from .messages import MessageParserError
20
+
21
+ if typing.TYPE_CHECKING:
22
+ from .devices import Device
23
+ from .messages import Message
18
24
 
19
- import logging
20
25
  logger = logging.getLogger(__name__)
21
26
 
22
- class AsyncEngine(core.EngineMixin):
23
27
 
24
- __slots__ = ['__txFifo','_loop','_tasks','_hooks','_watchdog_task','_kill_counter','running_event','watchdog_event','started_event']
28
+ #####################################################
29
+ # Hooks
30
+ #####################################################
31
+ class HookType(Enum):
32
+ start = 0
33
+ stop = 1
34
+
25
35
 
26
- def __init__(self,address=config.address,port=config.port,hops=config.hops,key=config.key):
27
- core.EngineMixin.__init__(self,address,port,hops,key)
36
+ class Hook(object):
37
+ __slots__ = ['type', 'func', 'args', 'kwargs']
38
+
39
+ def __init__(self, type_:HookType, func: core.FuncT, *args, **kwargs):
40
+ # func has to be a callable, but it can be a coroutine or a function
41
+ self.type = type_
42
+ self.func = func
43
+ self.args = args
44
+ self.kwargs = kwargs
28
45
 
29
- self.__txFifo = asyncio.Queue() # tx msg fifo
30
- self._loop = None # event loop
31
- self._hooks = [] # hooks
32
- self._tasks = [] # tasks
33
- self._watchdog_task = None # watchdog task
34
- self._kill_counter = 0 # watchdog counter
35
46
 
36
- self.started_event = asyncio.Event() # engine started event
37
- self.running_event = asyncio.Event() # engine running event
38
- self.watchdog_event = asyncio.Event() # watchdog event
47
+ class AsyncEngine(core.EngineMixin):
48
+ __slots__ = [
49
+ '__txFifo',
50
+ '_loop',
51
+ '_tasks',
52
+ '_hooks',
53
+ '_watchdog_task',
54
+ '_kill_counter',
55
+ 'running_event',
56
+ 'watchdog_event',
57
+ 'started_event',
58
+ ]
59
+
60
+ def __init__(
61
+ self, address: str = config.address, port: int = config.port, hops: int = config.hops, key: bytes = config.key):
62
+ core.EngineMixin.__init__(self, address, port, hops, key)
63
+
64
+ self.__txFifo = asyncio.Queue() # tx msg fifo
65
+ self._loop = None # event loop
66
+ self._hooks = [] # hooks
67
+ self._tasks = [] # tasks
68
+ self._watchdog_task = None # watchdog task
69
+ self._kill_counter = 0 # watchdog counter
70
+
71
+ self.started_event = asyncio.Event() # engine started event
72
+ self.running_event = asyncio.Event() # engine running event
73
+ self.watchdog_event = asyncio.Event() # watchdog event
39
74
 
40
75
  signal.signal(signal.SIGTERM, self.sigkill_handler)
41
76
  signal.signal(signal.SIGINT, self.sigkill_handler)
42
-
77
+
43
78
  # message receive workflow
44
79
  self.subscribe(self.handle_request)
45
80
  # start network
@@ -48,20 +83,20 @@ class AsyncEngine(core.EngineMixin):
48
83
  #####################################################
49
84
  # Hooks
50
85
  #####################################################
51
- def on_start(self,func,*args,**kwargs):
52
- hook = Hook(HookType.start,func,*args,**kwargs)
86
+ def on_start(self, func: core.FuncT, *args, **kwargs):
87
+ hook = Hook(HookType.start, func, *args, **kwargs)
53
88
  self._hooks.append(hook)
54
-
55
- def on_stop(self,func,*args,**kwargs):
56
- hook = Hook(HookType.stop,func,*args,**kwargs)
89
+
90
+ def on_stop(self, func: core.FuncT, *args, **kwargs):
91
+ hook = Hook(HookType.stop, func, *args, **kwargs)
57
92
  self._hooks.append(hook)
58
93
 
59
- async def run_hooks(self,hook_type):
60
- hooks = list(filter(lambda hook: hook.type==hook_type,self._hooks))
61
- if len(hooks)!=0:
94
+ async def run_hooks(self, hook_type: HookType):
95
+ hooks = list(filter(lambda hook: hook.type == hook_type, self._hooks))
96
+ if len(hooks) != 0:
62
97
  logger.debug(f"Launching {hook_type} hooks")
63
98
  for h in hooks:
64
- await run_func(h.func,*h.args,**h.kwargs)
99
+ await run_func(h.func, *h.args, **h.kwargs)
65
100
 
66
101
  #####################################################
67
102
  # timers
@@ -77,7 +112,7 @@ class AsyncEngine(core.EngineMixin):
77
112
  except CallbackError as e:
78
113
  logger.error(e.description)
79
114
  if t.counter != -1:
80
- t.counter-= 1
115
+ t.counter -= 1
81
116
  if t.counter == 0:
82
117
  expire_list.append(t)
83
118
  t.deadline = now + t.period
@@ -88,20 +123,20 @@ class AsyncEngine(core.EngineMixin):
88
123
  #####################################################
89
124
  # msg send / receive
90
125
  #####################################################
91
- def queue_msg(self, msg):
126
+ def queue_msg(self, msg: bytes):
92
127
  """queue a message"""
93
128
  self.__txFifo.put_nowait(msg)
94
129
 
95
- def send_msg(self, msg):
130
+ def send_msg(self, msg: bytes):
96
131
  """Send an encoded message to the bus, use queue_msg instead"""
97
132
  self.network.send(msg)
98
133
 
99
- async def receive_msg(self):
134
+ async def receive_msg(self) -> Optional['Message']:
100
135
  """return new received message or None"""
101
136
  data = await self.network.get_data()
102
137
  if data:
103
138
  try:
104
- msg = self.msg_factory.decode_msg(data,self.msg_filter)
139
+ msg = self.msg_factory.decode_msg(data, self.msg_filter)
105
140
  except MessageParserError as e:
106
141
  logger.warning(e)
107
142
  msg = None
@@ -113,27 +148,31 @@ class AsyncEngine(core.EngineMixin):
113
148
  msg = await self.receive_msg()
114
149
  if msg:
115
150
  for func in self.subscribers:
116
- await run_func(func,msg)
151
+ await run_func(func, msg)
117
152
  self.process_attributes_change()
118
153
 
119
- def handle_request(self, msg):
154
+ def handle_request(self, msg: 'Message'):
120
155
  """Filter msg for devices according default xAAL API then process the
121
156
  request for each targets identied in the engine
122
157
  """
123
158
  if not msg.is_request():
124
159
  return
160
+
125
161
  targets = core.filter_msg_for_devices(msg, self.devices)
126
162
  for target in targets:
127
- if msg.action == 'is_alive':
163
+ if msg.is_request_isalive():
128
164
  self.send_alive(target)
129
165
  else:
130
166
  self.new_task(self.handle_action_request(msg, target))
131
167
 
132
- async def handle_action_request(self, msg, target):
168
+ async def handle_action_request(self, msg: 'Message', target: 'Device'):
169
+ if msg.action is None:
170
+ return # should not happen, but pyright need this check
171
+
133
172
  try:
134
173
  result = await run_action(msg, target)
135
- if result != None:
136
- self.send_reply(dev=target,targets=[msg.source],action=msg.action,body=result)
174
+ if result is not None and type(result) is dict:
175
+ self.send_reply(dev=target, targets=[msg.source], action=msg.action, body=result)
137
176
  except CallbackError as e:
138
177
  self.send_error(target, e.code, e.description)
139
178
  except XAALError as e:
@@ -142,25 +181,25 @@ class AsyncEngine(core.EngineMixin):
142
181
  #####################################################
143
182
  # Asyncio loop & Tasks
144
183
  #####################################################
145
- def get_loop(self):
146
- if self._loop == None:
147
- logger.debug('New event loop')
184
+ def get_loop(self) -> asyncio.AbstractEventLoop:
185
+ if self._loop is None:
186
+ logger.debug("New event loop")
148
187
  self._loop = asyncio.get_event_loop()
149
188
  return self._loop
150
189
 
151
- def new_task(self,coro,name=None):
190
+ def new_task(self, coro: Any, name: Optional[str] = None) -> asyncio.Task:
152
191
  # we maintain a task list, to be able to stop/start the engine
153
192
  # on demand. needed by HASS
154
- task = self.get_loop().create_task(coro,name=name)
193
+ task = self.get_loop().create_task(coro, name=name)
155
194
  self._tasks.append(task)
156
195
  task.add_done_callback(self.task_callback)
157
196
  return task
158
197
 
159
- def task_callback(self, task):
198
+ def task_callback(self, task: asyncio.Task):
160
199
  # called when a task ended
161
200
  self._tasks.remove(task)
162
201
 
163
- def all_tasks(self):
202
+ def all_tasks(self) -> typing.List[asyncio.Task]:
164
203
  return self._tasks
165
204
 
166
205
  async def boot_task(self):
@@ -194,106 +233,105 @@ class AsyncEngine(core.EngineMixin):
194
233
  async def watchdog_task(self):
195
234
  await self.watchdog_event.wait()
196
235
  await self.stop()
197
- logger.info('Exit')
198
-
236
+ logger.info("Exit")
237
+
199
238
  #####################################################
200
239
  # start / stop / shutdown
201
240
  #####################################################
202
- def is_running(self):
241
+ def is_running(self) -> bool:
203
242
  return self.running_event.is_set()
204
243
 
205
244
  def start(self):
206
245
  if self.is_running():
207
- logger.warning('Engine already started')
246
+ logger.warning("Engine already started")
208
247
  return
209
248
  self.started_event.set()
210
- self.new_task(self.boot_task(),name='Boot')
211
- self.new_task(self.receive_task(),name='RecvQ')
212
- self.new_task(self.send_task(),name='SendQ')
213
- self.new_task(self.timer_task(),name='Timers')
214
- self.new_task(console(locals()),name='Console')
249
+ self.new_task(self.boot_task(), name='Boot')
250
+ self.new_task(self.receive_task(), name='RecvQ')
251
+ self.new_task(self.send_task(), name='SendQ')
252
+ self.new_task(self.timer_task(), name='Timers')
253
+ self.new_task(console(locals()), name='Console')
215
254
 
216
255
  def setup_alives_timer(self):
217
256
  # needed on stop-start sequence
218
257
  if self.process_alives in [t.func for t in self.timers]:
219
258
  return
220
259
  # process alives every 10 seconds
221
- self.add_timer(self.process_alives,10)
260
+ self.add_timer(self.process_alives, 10)
222
261
 
223
- async def stop(self):
224
- logger.info('Stopping engine')
262
+ async def stop(self): # pyright: ignore
263
+ logger.info("Stopping engine")
225
264
  await self.run_hooks(HookType.stop)
226
265
  self.running_event.clear()
227
266
  self.started_event.clear()
228
267
  # cancel all tasks
229
268
  for task in self.all_tasks():
230
- if task!=self._watchdog_task:
269
+ if task != self._watchdog_task:
231
270
  task.cancel()
232
271
  await asyncio.sleep(0.1)
233
272
 
234
- def sigkill_handler(self,signal,frame):
235
- print("", end = "\r") #remove the uggly ^C
273
+ def sigkill_handler(self, signal, frame):
274
+ print("", end="\r") # remove the uggly ^C
236
275
  if not self.is_running():
237
- logger.warning('Engine already stopped')
276
+ logger.warning("Engine already stopped")
238
277
  self._kill_counter = 1
239
- self._kill_counter +=1
278
+ self._kill_counter += 1
240
279
  self.shutdown()
241
280
  if self._kill_counter > 1:
242
- logger.warning('Force quit')
281
+ logger.warning("Force quit")
243
282
  sys.exit(-1)
244
283
  else:
245
- logger.warning('Kill requested')
284
+ logger.warning("Kill requested")
246
285
 
247
286
  def shutdown(self):
248
287
  self.watchdog_event.set()
249
-
288
+
250
289
  def run(self):
251
290
  if not self.started_event.is_set():
252
291
  self.start()
253
- if self._watchdog_task == None:
292
+ if self._watchdog_task is None:
254
293
  # start the watchdog task
255
- self._watchdog_task = self.new_task(self.watchdog_task(),name='Watchdog task')
294
+ self._watchdog_task = self.new_task(self.watchdog_task(), name="Watchdog task")
256
295
  self.get_loop().run_until_complete(self._watchdog_task)
257
296
  else:
258
- logger.warning('Engine already running')
297
+ logger.warning("Engine already running")
259
298
 
260
299
  #####################################################
261
300
  # Debugging tools
262
301
  #####################################################
263
302
  def dump_timers(self):
264
- headers = ['Func','Period','Counter','Deadline']
303
+ headers = ['Func', 'Period', 'Counter', 'Deadline']
265
304
  rows = []
266
305
  now = time.time()
267
306
  for t in self.timers:
268
- remain = round(t.deadline-now,1)
269
- rows.append([str(t.func),t.period,t.counter,remain])
270
- print('= Timers')
271
- print(tabulate(rows,headers=headers,tablefmt="fancy_grid"))
272
-
307
+ remain = round(t.deadline - now, 1)
308
+ rows.append([str(t.func), t.period, t.counter, remain])
309
+ print("= Timers")
310
+ print(tabulate(rows, headers=headers, tablefmt='fancy_grid'))
273
311
 
274
312
  def dump_tasks(self):
275
- headers = ["Name","Coro","Loop ID"]
313
+ headers = ['Name', 'Coro', 'Loop ID']
276
314
  rows = []
277
315
  for t in self.all_tasks():
278
- rows.append([t.get_name(),str(t.get_coro()),id(t.get_loop())])
279
- print('= Tasks')
280
- print(tabulate(rows,headers=headers,tablefmt="fancy_grid"))
316
+ rows.append([t.get_name(), str(t.get_coro()), id(t.get_loop())])
317
+ print("= Tasks")
318
+ print(tabulate(rows, headers=headers, tablefmt='fancy_grid'))
281
319
 
282
320
  def dump_devices(self):
283
- headers = ["addr","dev_type","info"]
321
+ headers = ['addr', 'dev_type', 'info']
284
322
  rows = []
285
323
  for d in self.devices:
286
- rows.append([d.address,d.dev_type,d.info])
287
- print('= Devices')
288
- print(tabulate(rows,headers=headers,tablefmt="fancy_grid"))
324
+ rows.append([d.address, d.dev_type, d.info])
325
+ print("= Devices")
326
+ print(tabulate(rows, headers=headers, tablefmt='fancy_grid'))
289
327
 
290
328
  def dump_hooks(self):
291
- headers = ["Type","Hook"]
329
+ headers = ['Type', 'Hook']
292
330
  rows = []
293
331
  for h in self._hooks:
294
- rows.append([h.type,str(h.func)])
295
- print('= Hooks')
296
- print(tabulate(rows,headers=headers,tablefmt="fancy_grid"))
332
+ rows.append([h.type, str(h.func)])
333
+ print("= Hooks")
334
+ print(tabulate(rows, headers=headers, tablefmt='fancy_grid'))
297
335
 
298
336
  def dump(self):
299
337
  self.dump_devices()
@@ -301,8 +339,8 @@ class AsyncEngine(core.EngineMixin):
301
339
  self.dump_timers()
302
340
  self.dump_hooks()
303
341
 
304
- def get_device(self,uuid):
305
- uuid = tools.get_uuid(uuid)
342
+ def get_device(self, uuid: UUID) -> Optional['Device']:
343
+ # TODO:Check if this method is usefull
306
344
  for dev in self.devices:
307
345
  if dev.address == uuid:
308
346
  return dev
@@ -312,26 +350,26 @@ class AsyncEngine(core.EngineMixin):
312
350
  #####################################################
313
351
  # Utilities functions
314
352
  #####################################################
315
- async def run_func(func,*args,**kwargs):
316
- """run a function or a coroutine function """
353
+ async def run_func(func, *args, **kwargs):
354
+ """run a function or a coroutine function"""
317
355
  if asyncio.iscoroutinefunction(func):
318
- return await func(*args,**kwargs)
356
+ return await func(*args, **kwargs)
319
357
  else:
320
- return func(*args,**kwargs)
358
+ return func(*args, **kwargs)
321
359
 
322
360
 
323
- async def run_action(msg,device):
324
- """
361
+ async def run_action(msg: 'Message', device: 'Device'):
362
+ """
325
363
  Extract an action & launch it
326
364
  Return:
327
365
  - action result
328
366
  - None if no result
329
367
 
330
- Notes:
368
+ Notes:
331
369
  - If an exception raised, it's logged, and raise an XAALError.
332
370
  - Same API as legacy Engine, but accept coroutine functions
333
371
  """
334
- method,params = core.search_action(msg,device)
372
+ method, params = core.search_action(msg, device)
335
373
  result = None
336
374
  try:
337
375
  if asyncio.iscoroutinefunction(method):
@@ -340,49 +378,35 @@ async def run_action(msg,device):
340
378
  result = method(**params)
341
379
  except Exception as e:
342
380
  logger.error(e)
343
- raise XAALError("Error in method:%s params:%s" % (msg.action,params))
381
+ raise XAALError("Error in method:%s params:%s" % (msg.action, params))
344
382
  return result
345
383
 
346
384
 
347
- #####################################################
348
- # Hooks
349
- #####################################################
350
- class HookType(Enum):
351
- start = 0
352
- stop = 1
353
-
354
- class Hook(object):
355
- __slots__ = ['type','func','args','kwargs']
356
- def __init__(self,type_,func,*args,**kwargs):
357
- self.type = type_
358
- self.func = func
359
- self.args = args
360
- self.kwargs = kwargs
361
-
362
-
363
385
  #####################################################
364
386
  # Debugging console
365
387
  #####################################################
366
- async def console(locals=locals(),port=None):
388
+ async def console(locals=locals(), port: Optional[int] = None):
367
389
  """launch a console to enable remote engine inspection"""
368
- if port == None:
390
+ if port is None:
369
391
  # let's find a free port if not specified
370
392
  def find_free_port():
371
393
  import socketserver
372
- with socketserver.TCPServer(("localhost", 0), None) as s:
394
+ with socketserver.TCPServer(('localhost', 0), None) as s: # pyright: ignore pyright reject the None here
373
395
  return s.server_address[1]
396
+
374
397
  port = find_free_port()
375
-
376
- logger.debug(f'starting debug console on port {port}')
377
- sys.ps1 = '[xAAL] >>> '
378
- banner = '=' * 78 +"\nxAAL remote console\n" + '=' *78
379
- locals.update({'pprint':pprint})
398
+
399
+ logger.debug(f"starting debug console on port {port}")
400
+ sys.ps1 = "[xAAL] >>> "
401
+ banner = "=" * 78 + "\nxAAL remote console\n" + "=" * 78
402
+ locals.update({'pprint': pprint})
380
403
 
381
404
  def factory(streams):
382
405
  return aioconsole.AsynchronousConsole(locals=locals, streams=streams)
406
+
383
407
  # start the console
384
408
  try:
385
409
  # debian with ipv6 disabled still state that localhost is ::1, which broke aioconsole
386
- await aioconsole.start_interactive_server(host='127.0.0.1', port=port,factory=factory,banner=banner)
410
+ await aioconsole.start_interactive_server(host="127.0.0.1", port=port, factory=factory, banner=banner) # pyright: ignore
387
411
  except OSError:
388
- logger.warning('Unable to run console')
412
+ logger.warning("Unable to run console")
xaal/lib/aiohelpers.py CHANGED
@@ -4,8 +4,8 @@ import asyncio
4
4
  import logging
5
5
 
6
6
  @decorator
7
- def spawn(func,*args,**kwargs):
8
- return asyncio.get_event_loop().run_in_executor(None,func,*args,**kwargs)
7
+ def spawn(func, *args, **kwargs):
8
+ return asyncio.get_event_loop().run_in_executor(None, func, *args, **kwargs)
9
9
 
10
10
 
11
11
  def static_vars(**kwargs_):
@@ -16,20 +16,20 @@ def static_vars(**kwargs_):
16
16
  return decorate
17
17
 
18
18
 
19
- def run_async_package(pkg_name,pkg_setup,console_log = True,file_log=False):
19
+ def run_async_package(pkg_name, pkg_setup, console_log=True, file_log=False):
20
20
  if console_log:
21
21
  set_console_title(pkg_name)
22
22
  setup_console_logger()
23
23
  if file_log:
24
24
  setup_file_logger(pkg_name)
25
-
26
- from .aioengine import AsyncEngine
25
+
26
+ from .aioengine import AsyncEngine
27
27
  eng = AsyncEngine()
28
28
  eng.start()
29
29
  logger = logging.getLogger(pkg_name)
30
- logger.info('starting xaal package: %s'% pkg_name )
30
+ logger.info("starting xaal package: %s"% pkg_name )
31
31
  result = pkg_setup(eng)
32
- if result != True:
32
+ if result is not True:
33
33
  logger.critical("something goes wrong with package: %s" % pkg_name)
34
34
  try:
35
35
  eng.run()
xaal/lib/aionetwork.py CHANGED
@@ -1,14 +1,13 @@
1
1
  import asyncio
2
- import struct
3
- import socket
4
-
5
2
  import logging
3
+ import socket
4
+ import struct
6
5
 
7
6
  logger = logging.getLogger(__name__)
8
7
 
9
- class AsyncNetworkConnector(object):
10
8
 
11
- def __init__(self, addr, port, hops,bind_addr='0.0.0.0'):
9
+ class AsyncNetworkConnector(object):
10
+ def __init__(self, addr: str, port: int, hops: int, bind_addr="0.0.0.0"):
12
11
  self.addr = addr
13
12
  self.port = port
14
13
  self.hops = hops
@@ -18,57 +17,58 @@ class AsyncNetworkConnector(object):
18
17
  async def connect(self):
19
18
  loop = asyncio.get_running_loop()
20
19
  on_con_lost = loop.create_future()
21
- self.transport, self.protocol = await loop.create_datagram_endpoint(
22
- lambda: XAALServerProtocol(on_con_lost,self.receive), sock = self.new_sock())
20
+ self.transport, self.protocol = await loop.create_datagram_endpoint(
21
+ lambda: XAALServerProtocol(on_con_lost, self.receive), sock=self.new_sock()
22
+ )
23
23
  # In some conditions (containers), transport is connected but IGMP is delayed (up to 10ms)
24
24
  # so we need to wait for IGMP to be really sent.
25
25
  await asyncio.sleep(0.05)
26
26
 
27
27
  def new_sock(self):
28
- sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP)
28
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
29
29
  try:
30
30
  # Linux + MacOS + BSD
31
31
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
32
- except:
32
+ except Exception:
33
33
  # Windows
34
34
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
35
35
  sock.bind((self.bind_addr, self.port))
36
- mreq = struct.pack("=4s4s",socket.inet_aton(self.addr),socket.inet_aton(self.bind_addr))
37
- sock.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,mreq)
38
- sock.setsockopt(socket.IPPROTO_IP,socket.IP_MULTICAST_TTL,10)
36
+ mreq = struct.pack("=4s4s", socket.inet_aton(self.addr), socket.inet_aton(self.bind_addr))
37
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
38
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 10)
39
39
  sock.setblocking(False)
40
40
  return sock
41
41
 
42
- def send(self,data):
43
- self.protocol.datagram_send(data,self.addr,self.port)
42
+ def send(self, data: bytes):
43
+ self.protocol.datagram_send(data, self.addr, self.port)
44
44
 
45
- def receive(self,data):
45
+ def receive(self, data: bytes):
46
46
  self._rx_queue.put_nowait(data)
47
47
 
48
- async def get_data(self):
48
+ async def get_data(self) -> bytes:
49
49
  return await self._rx_queue.get()
50
50
 
51
+
51
52
  class XAALServerProtocol(asyncio.Protocol):
52
- def __init__(self,on_con_lost,on_dtg_recv):
53
+ def __init__(self, on_con_lost, on_dtg_recv):
53
54
  self.on_con_lost = on_con_lost
54
55
  self.on_dtg_recv = on_dtg_recv
55
-
56
+
56
57
  def connection_made(self, transport):
57
58
  logger.info("xAAL network connected")
58
59
  self.transport = transport
59
60
 
60
61
  def error_received(self, exc):
61
- print('Error received:', exc)
62
+ print("Error received:", exc)
62
63
  logger.warning(f"Error received: {exc}")
63
64
 
64
65
  def connection_lost(self, exc):
65
66
  logger.info(f"Connexion closed: {exc}")
66
67
  self.on_con_lost.set_result(True)
67
-
68
- def datagram_send(self,data,ip,port):
69
- self.transport.sendto(data,(ip,port))
68
+
69
+ def datagram_send(self, data, ip, port):
70
+ self.transport.sendto(data, (ip, port))
70
71
 
71
72
  def datagram_received(self, data, addr):
72
- #print(f"pkt from {addr}")
73
+ # print(f"pkt from {addr}")
73
74
  self.on_dtg_recv(data)
74
-