xaal.tools 0.4__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.
- xaal_tools-0.4/PKG-INFO +12 -0
- xaal_tools-0.4/README.rst +0 -0
- xaal_tools-0.4/pyproject.toml +29 -0
- xaal_tools-0.4/setup.cfg +4 -0
- xaal_tools-0.4/xaal/tools/__init__.py +1 -0
- xaal_tools-0.4/xaal/tools/keygen.py +17 -0
- xaal_tools-0.4/xaal/tools/toolbox.py +1004 -0
- xaal_tools-0.4/xaal/tools/uuidgen.py +25 -0
- xaal_tools-0.4/xaal.tools.egg-info/PKG-INFO +12 -0
- xaal_tools-0.4/xaal.tools.egg-info/SOURCES.txt +12 -0
- xaal_tools-0.4/xaal.tools.egg-info/dependency_links.txt +1 -0
- xaal_tools-0.4/xaal.tools.egg-info/entry_points.txt +14 -0
- xaal_tools-0.4/xaal.tools.egg-info/requires.txt +2 -0
- xaal_tools-0.4/xaal.tools.egg-info/top_level.txt +1 -0
xaal_tools-0.4/PKG-INFO
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: xaal.tools
|
|
3
|
+
Version: 0.4
|
|
4
|
+
Summary: xAAL devices tools
|
|
5
|
+
Author-email: Jerome Kerdreux <Jerome.Kerdreux@imt-atlantique.fr>
|
|
6
|
+
License: GPL License
|
|
7
|
+
Keywords: xaal,tools
|
|
8
|
+
Classifier: Programming Language :: Python
|
|
9
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
10
|
+
Description-Content-Type: text/x-rst
|
|
11
|
+
Requires-Dist: xaal.lib
|
|
12
|
+
Requires-Dist: colored==1.4.3
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "xaal.tools"
|
|
3
|
+
version = "0.4"
|
|
4
|
+
description = "xAAL devices tools"
|
|
5
|
+
readme = "README.rst"
|
|
6
|
+
authors = [ { name = "Jerome Kerdreux", email = "Jerome.Kerdreux@imt-atlantique.fr" } ]
|
|
7
|
+
license = { text = "GPL License"}
|
|
8
|
+
classifiers = ["Programming Language :: Python",
|
|
9
|
+
"Topic :: Software Development :: Libraries :: Python Modules"]
|
|
10
|
+
keywords = ["xaal", "tools"]
|
|
11
|
+
dependencies = ["xaal.lib", "colored==1.4.3"]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
xaal-isalive = "xaal.tools.toolbox:is_alive"
|
|
15
|
+
xaal-info = "xaal.tools.toolbox:info"
|
|
16
|
+
xaal-walker = "xaal.tools.toolbox:walker"
|
|
17
|
+
xaal-dumper = "xaal.tools.toolbox:dumper"
|
|
18
|
+
xaal-log = "xaal.tools.toolbox:log"
|
|
19
|
+
xaal-querydb = "xaal.tools.toolbox:query_db"
|
|
20
|
+
xaal-cleandb = "xaal.tools.toolbox:clean_db"
|
|
21
|
+
xaal-send = "xaal.tools.toolbox:send"
|
|
22
|
+
xaal-tail = "xaal.tools.toolbox:tail"
|
|
23
|
+
xaal-pkgrun = "xaal.tools.toolbox:pkgrun"
|
|
24
|
+
xaal-keygen = "xaal.tools.keygen:main"
|
|
25
|
+
xaal-uuidgen = "xaal.tools.uuidgen:main"
|
|
26
|
+
xaal-shell = "xaal.tools.toolbox:shell"
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages.find]
|
|
29
|
+
include = ["xaal.tools"]
|
xaal_tools-0.4/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
""" Tool to build a key pass for xAAL config file"""
|
|
2
|
+
|
|
3
|
+
from xaal.lib import tools
|
|
4
|
+
import binascii
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
try:
|
|
8
|
+
temp = input("Please enter your passphrase: ")
|
|
9
|
+
key = tools.pass2key(temp)
|
|
10
|
+
print("Cut & Paste this key in your xAAL config-file")
|
|
11
|
+
print("key=%s"% binascii.hexlify(key).decode('utf-8'))
|
|
12
|
+
except KeyboardInterrupt:
|
|
13
|
+
print("Bye Bye..")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if __name__ == '__main__':
|
|
17
|
+
main()
|
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides a set of utilities to interact with the xAAL bus and devices.
|
|
3
|
+
The module use a lot of AsyncEngine features, so the code can be a bit tricky to read.
|
|
4
|
+
If you're looking for simples examples, please check the legacy tools instead.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
if sys.argv[0].endswith('pkgrun'):
|
|
10
|
+
# right now, some packages depend on gevent, so we need to import it here.
|
|
11
|
+
# this is only needed for the pkgrun command
|
|
12
|
+
try:
|
|
13
|
+
from gevent import monkey
|
|
14
|
+
monkey.patch_all(thread=False)
|
|
15
|
+
# print("Loaded gevent")
|
|
16
|
+
except ModuleNotFoundError:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
# xAAL import
|
|
20
|
+
from xaal.lib import AsyncEngine, Device, tools, helpers, config
|
|
21
|
+
from xaal.lib.messages import MessageType
|
|
22
|
+
|
|
23
|
+
# General python import
|
|
24
|
+
import asyncio
|
|
25
|
+
import time
|
|
26
|
+
import importlib
|
|
27
|
+
import logging
|
|
28
|
+
import enum
|
|
29
|
+
import optparse
|
|
30
|
+
|
|
31
|
+
# colors & styles
|
|
32
|
+
from colored import fore, style
|
|
33
|
+
from tabulate import tabulate
|
|
34
|
+
import pprint
|
|
35
|
+
import shutil # needed by the tail command
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
HIDE_ACTION = ['get_attributes', 'get_description', 'get_keys_values', 'get_devices', 'is_alive']
|
|
39
|
+
TABLE_STYLE = 'psql'
|
|
40
|
+
LINE = "="*78
|
|
41
|
+
DB_DEV_TYPE = "metadatadb.basic"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Colors(enum.Enum):
|
|
45
|
+
DEFAULT = fore.WHITE
|
|
46
|
+
# ALIVE = fore.LIGHT_GRAY
|
|
47
|
+
# ATTRIBUTS = fore.LIGHT_YELLOW
|
|
48
|
+
# REQUEST = fore.LIGHT_RED
|
|
49
|
+
# IS_ALIVE = fore.LIGHT_MAGENTA
|
|
50
|
+
# REPLY = fore.LIGHT_CYAN
|
|
51
|
+
# NOTIFY = fore.LIGHT_GREEN
|
|
52
|
+
# DEV_TYPE = fore.LIGHT_BLUE
|
|
53
|
+
# ADDR = fore.LIGHT_RED
|
|
54
|
+
# INFO = fore.CYAN
|
|
55
|
+
# DB = fore.SPRING_GREEN_1
|
|
56
|
+
|
|
57
|
+
ALIVE = fore.LIGHT_GRAY
|
|
58
|
+
ATTRIBUTS = fore.YELLOW
|
|
59
|
+
REQUEST = fore.RED
|
|
60
|
+
IS_ALIVE = fore.MAGENTA
|
|
61
|
+
REPLY = fore.CYAN
|
|
62
|
+
NOTIFY = fore.LIGHT_GREEN
|
|
63
|
+
DEV_TYPE = fore.BLUE
|
|
64
|
+
ADDR = fore.RED
|
|
65
|
+
INFO = fore.CYAN
|
|
66
|
+
DB = fore.SPRING_GREEN_1
|
|
67
|
+
|
|
68
|
+
def __str__(self):
|
|
69
|
+
return self.value
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class DeviceInfo(object):
|
|
73
|
+
def __init__(self):
|
|
74
|
+
self.alive = False
|
|
75
|
+
self.address = None
|
|
76
|
+
self.dev_type = None
|
|
77
|
+
self.description = None
|
|
78
|
+
self.attributes = None
|
|
79
|
+
self.db = None
|
|
80
|
+
self.ready_event = asyncio.Event()
|
|
81
|
+
self.displayed = False
|
|
82
|
+
|
|
83
|
+
def ready(self):
|
|
84
|
+
if self.address is None:
|
|
85
|
+
return False
|
|
86
|
+
if self.dev_type is None:
|
|
87
|
+
return False
|
|
88
|
+
if self.description is None:
|
|
89
|
+
return False
|
|
90
|
+
if self.attributes is None:
|
|
91
|
+
return False
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
def display(self, color=True):
|
|
95
|
+
if color:
|
|
96
|
+
self.color_display()
|
|
97
|
+
else:
|
|
98
|
+
self.normal_display()
|
|
99
|
+
|
|
100
|
+
def color_display(self):
|
|
101
|
+
# info & description
|
|
102
|
+
r = []
|
|
103
|
+
r.append(['Informations', ''])
|
|
104
|
+
r.append(['============', ''])
|
|
105
|
+
r.append(["alive", colorize(Colors.IS_ALIVE, self.alive)])
|
|
106
|
+
r.append(['dev_type', colorize(Colors.DEV_TYPE, self.dev_type)])
|
|
107
|
+
r.append(['address', colorize(Colors.ADDR, self.address)])
|
|
108
|
+
if self.description and len(self.description) > 0:
|
|
109
|
+
for k, v in self.description.items():
|
|
110
|
+
if k == 'info':
|
|
111
|
+
v = colorize(Colors.INFO, v)
|
|
112
|
+
else:
|
|
113
|
+
v = colorize(Colors.DEFAULT, v)
|
|
114
|
+
r.append([k, v])
|
|
115
|
+
|
|
116
|
+
# attributes
|
|
117
|
+
if self.attributes and len(self.attributes) > 0:
|
|
118
|
+
# tabulate has no minimal width so used this trick
|
|
119
|
+
r.append(['-'*22, '-'*46])
|
|
120
|
+
r.append(['Attributes', ''])
|
|
121
|
+
r.append(['==========', ''])
|
|
122
|
+
for k, v in self.attributes.items():
|
|
123
|
+
v = pprint.pformat(v, width=55).split('\n')
|
|
124
|
+
tmp = ''
|
|
125
|
+
for line in v:
|
|
126
|
+
tmp = tmp + colorize(Colors.ATTRIBUTS, line) + '\n'
|
|
127
|
+
r.append([k, tmp])
|
|
128
|
+
|
|
129
|
+
# metadata
|
|
130
|
+
if self.db and len(self.db.keys()) > 0:
|
|
131
|
+
r.append(['-'*22, '-'*46])
|
|
132
|
+
r.append(['Metadata', ''])
|
|
133
|
+
r.append(['========', ''])
|
|
134
|
+
for k, v in self.db.items():
|
|
135
|
+
v = colorize(Colors.DB, v)
|
|
136
|
+
r.append([k, v])
|
|
137
|
+
print(tabulate(r, tablefmt=TABLE_STYLE))
|
|
138
|
+
|
|
139
|
+
def normal_display(self):
|
|
140
|
+
# info & description
|
|
141
|
+
r = []
|
|
142
|
+
r.append(['Informations', ''])
|
|
143
|
+
r.append(['============', ''])
|
|
144
|
+
r.append(["alive", self.alive])
|
|
145
|
+
r.append(['dev_type', self.dev_type])
|
|
146
|
+
r.append(['address', self.address])
|
|
147
|
+
for k, v in self.description.items():
|
|
148
|
+
r.append([k, v])
|
|
149
|
+
|
|
150
|
+
# attributes
|
|
151
|
+
if len(self.attributes) > 0:
|
|
152
|
+
# tabulate has no minimal width so used this trick
|
|
153
|
+
r.append(['-'*22, '-'*46])
|
|
154
|
+
r.append(['Attributes', ''])
|
|
155
|
+
r.append(['==========', ''])
|
|
156
|
+
for k, v in self.attributes.items():
|
|
157
|
+
v = pprint.pformat(v, width=55).split('\n')
|
|
158
|
+
tmp = ''
|
|
159
|
+
for line in v:
|
|
160
|
+
tmp = tmp + line + '\n'
|
|
161
|
+
r.append([k, tmp])
|
|
162
|
+
# metadata
|
|
163
|
+
if self.db and len(self.db.keys()) > 0:
|
|
164
|
+
r.append(['-'*22, '-'*46])
|
|
165
|
+
r.append(['Metadata', ''])
|
|
166
|
+
r.append(['========', ''])
|
|
167
|
+
for k, v in self.db.items():
|
|
168
|
+
r.append([k, v])
|
|
169
|
+
print(tabulate(r, tablefmt=TABLE_STYLE))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class ToolboxHelper(object):
|
|
173
|
+
def __init__(self) -> None:
|
|
174
|
+
self.name = None # cmdline name
|
|
175
|
+
self.parser = None
|
|
176
|
+
self.engine = None # toolbox engine
|
|
177
|
+
self.device = None # current device
|
|
178
|
+
self.devices = [] # devices list (alive / walker)
|
|
179
|
+
# idle detector / force exit
|
|
180
|
+
self.exit_event = asyncio.Event()
|
|
181
|
+
self.last_msg_time = now()
|
|
182
|
+
|
|
183
|
+
# display time
|
|
184
|
+
self.start_time = 0
|
|
185
|
+
|
|
186
|
+
# db server
|
|
187
|
+
self.db_server = None
|
|
188
|
+
self.db_server_found = asyncio.Event()
|
|
189
|
+
|
|
190
|
+
# Let's start
|
|
191
|
+
self.setup_name()
|
|
192
|
+
self.setup_parser()
|
|
193
|
+
|
|
194
|
+
def setup_name(self):
|
|
195
|
+
name = sys.argv[0].split('/')[-1]
|
|
196
|
+
# helpers.set_console_title(name)
|
|
197
|
+
self.name = name
|
|
198
|
+
return self.name
|
|
199
|
+
|
|
200
|
+
def setup_parser(self, usage=None):
|
|
201
|
+
self.parser = optparse.OptionParser(usage=usage)
|
|
202
|
+
self.parser.add_option("-c", dest="no_color", help="disable color", action="store_true", default=False)
|
|
203
|
+
self.parser.add_option("-l", dest="debug", help="Enable logging", action="store_true", default=False)
|
|
204
|
+
self.parser.add_option("-a", dest="mcast_addr", help="Multicast address", default=config.address)
|
|
205
|
+
self.parser.add_option("-p", dest="mcast_port", help="Multicast port", default=config.port)
|
|
206
|
+
return self.parser
|
|
207
|
+
|
|
208
|
+
def setup_device(self):
|
|
209
|
+
# toolbox device
|
|
210
|
+
dev = Device("cli.basic")
|
|
211
|
+
dev.address = tools.get_random_uuid()
|
|
212
|
+
dev.info = f'Aiotoolbox CLI {self.name}'
|
|
213
|
+
self.device = dev
|
|
214
|
+
self.engine.add_device(self.device)
|
|
215
|
+
return self.device
|
|
216
|
+
|
|
217
|
+
def setup_engine(self):
|
|
218
|
+
# engine
|
|
219
|
+
bus_addr = self.options.mcast_addr
|
|
220
|
+
try:
|
|
221
|
+
bus_port = int(self.options.mcast_port)
|
|
222
|
+
except ValueError:
|
|
223
|
+
self.error("Invalid port number")
|
|
224
|
+
eng = AsyncEngine(address=bus_addr, port=bus_port)
|
|
225
|
+
eng.disable_msg_filter()
|
|
226
|
+
# start the engine
|
|
227
|
+
self.engine = eng
|
|
228
|
+
self.start_time = now()
|
|
229
|
+
eng.start()
|
|
230
|
+
return eng
|
|
231
|
+
|
|
232
|
+
def setup_basic(self):
|
|
233
|
+
eng = self.setup_engine()
|
|
234
|
+
dev = self.setup_device()
|
|
235
|
+
return (eng, dev)
|
|
236
|
+
|
|
237
|
+
#####################################################
|
|
238
|
+
# command line parsing
|
|
239
|
+
#####################################################
|
|
240
|
+
def setup_msg_parser(self):
|
|
241
|
+
self.engine.subscribe(self.parse_msg)
|
|
242
|
+
|
|
243
|
+
def parse(self):
|
|
244
|
+
self.options, self.args = self.parser.parse_args()
|
|
245
|
+
if self.options.debug:
|
|
246
|
+
helpers.setup_console_logger()
|
|
247
|
+
return self.options, self.args
|
|
248
|
+
|
|
249
|
+
def check_address(self, value):
|
|
250
|
+
addr = None
|
|
251
|
+
if value:
|
|
252
|
+
addr = tools.get_uuid(value)
|
|
253
|
+
if addr is None:
|
|
254
|
+
self.error(f"Invalid address: {value}")
|
|
255
|
+
return addr
|
|
256
|
+
|
|
257
|
+
def check_devtype(self, value):
|
|
258
|
+
dev_type = 'any.any'
|
|
259
|
+
if value:
|
|
260
|
+
if not tools.is_valid_dev_type(value):
|
|
261
|
+
self.error("Invalid device type: %s" % value)
|
|
262
|
+
dev_type = value
|
|
263
|
+
return dev_type
|
|
264
|
+
|
|
265
|
+
#####################################################
|
|
266
|
+
# devices
|
|
267
|
+
#####################################################
|
|
268
|
+
def add_device(self, dev):
|
|
269
|
+
if dev.address:
|
|
270
|
+
if self.get_device(dev.address):
|
|
271
|
+
# device already know
|
|
272
|
+
return
|
|
273
|
+
self.devices.append(dev)
|
|
274
|
+
|
|
275
|
+
def get_device(self, addr):
|
|
276
|
+
for k in self.devices:
|
|
277
|
+
if k.address == addr:
|
|
278
|
+
return k
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
def new_device(self, addr):
|
|
282
|
+
dev = DeviceInfo()
|
|
283
|
+
dev.address = addr
|
|
284
|
+
self.add_device(dev)
|
|
285
|
+
return dev
|
|
286
|
+
|
|
287
|
+
def display_device(self, dev):
|
|
288
|
+
# show device only once
|
|
289
|
+
if dev.displayed:
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
if self.options.no_color:
|
|
293
|
+
dev.normal_display()
|
|
294
|
+
else:
|
|
295
|
+
dev.color_display()
|
|
296
|
+
dev.displayed = True
|
|
297
|
+
|
|
298
|
+
def is_ready(self, dev):
|
|
299
|
+
if self.db_server:
|
|
300
|
+
if dev.db is None:
|
|
301
|
+
return False
|
|
302
|
+
return dev.ready()
|
|
303
|
+
|
|
304
|
+
#####################################################
|
|
305
|
+
# messages
|
|
306
|
+
#####################################################
|
|
307
|
+
def dump_msg(self, msg):
|
|
308
|
+
color = not self.options.no_color
|
|
309
|
+
color_value = self.color_for_msg(msg)
|
|
310
|
+
if color:
|
|
311
|
+
print(color_value, end='')
|
|
312
|
+
msg.dump()
|
|
313
|
+
if color:
|
|
314
|
+
print(style.RESET, end='')
|
|
315
|
+
|
|
316
|
+
def color_for_msg(self, msg):
|
|
317
|
+
color_value = Colors.DEFAULT
|
|
318
|
+
if msg.is_request_isalive():
|
|
319
|
+
color_value = Colors.IS_ALIVE
|
|
320
|
+
elif msg.is_alive():
|
|
321
|
+
color_value = Colors.ALIVE
|
|
322
|
+
elif msg.is_attributes_change():
|
|
323
|
+
color_value = Colors.ATTRIBUTS
|
|
324
|
+
elif msg.is_request():
|
|
325
|
+
color_value = Colors.REQUEST
|
|
326
|
+
elif msg.is_reply():
|
|
327
|
+
color_value = Colors.REPLY
|
|
328
|
+
elif msg.is_notify():
|
|
329
|
+
color_value = Colors.NOTIFY
|
|
330
|
+
return color_value
|
|
331
|
+
|
|
332
|
+
def parse_msg(self, msg):
|
|
333
|
+
""" default parser used for info/walker"""
|
|
334
|
+
target = self.get_device(msg.source)
|
|
335
|
+
if target is None:
|
|
336
|
+
target = DeviceInfo()
|
|
337
|
+
target.address = msg.source
|
|
338
|
+
target.dev_type = msg.dev_type
|
|
339
|
+
self.add_device(target)
|
|
340
|
+
if target.dev_type is None:
|
|
341
|
+
target.dev_type = msg.dev_type
|
|
342
|
+
if msg.is_get_attribute_reply():
|
|
343
|
+
target.attributes = msg.body
|
|
344
|
+
elif msg.is_get_description_reply():
|
|
345
|
+
target.description = msg.body
|
|
346
|
+
elif msg.is_alive():
|
|
347
|
+
target.alive = True
|
|
348
|
+
return target
|
|
349
|
+
|
|
350
|
+
def parse_db_msg(self, msg):
|
|
351
|
+
found = msg.body.get('device', None)
|
|
352
|
+
found_map = msg.body.get('map', None)
|
|
353
|
+
tmp = self.get_device(found)
|
|
354
|
+
if not tmp:
|
|
355
|
+
tmp = self.new_device(found)
|
|
356
|
+
tmp.db = found_map
|
|
357
|
+
return tmp
|
|
358
|
+
|
|
359
|
+
def request_info(self, addr):
|
|
360
|
+
self.engine.send_get_description(self.device, [addr])
|
|
361
|
+
self.engine.send_get_attributes(self.device, [addr])
|
|
362
|
+
|
|
363
|
+
def request_is_alive(self, addr=None, dev_type=None):
|
|
364
|
+
if addr:
|
|
365
|
+
self.engine.send_is_alive(self.device, [addr])
|
|
366
|
+
elif dev_type:
|
|
367
|
+
self.engine.send_is_alive(self.device, dev_types=[dev_type])
|
|
368
|
+
else:
|
|
369
|
+
self.engine.send_is_alive(self.device)
|
|
370
|
+
|
|
371
|
+
def request_action(self, addr, action, body=None):
|
|
372
|
+
self.engine.send_request(self.device, [addr], action, body)
|
|
373
|
+
|
|
374
|
+
#####################################################
|
|
375
|
+
# db server
|
|
376
|
+
#####################################################
|
|
377
|
+
def find_db_callback(self, msg):
|
|
378
|
+
if not match_dev_type(msg, DB_DEV_TYPE):
|
|
379
|
+
return
|
|
380
|
+
# new db server found
|
|
381
|
+
if msg.is_alive():
|
|
382
|
+
self.db_server = msg.source
|
|
383
|
+
self.db_server_found.set()
|
|
384
|
+
|
|
385
|
+
async def find_db_server(self):
|
|
386
|
+
self.engine.subscribe(self.find_db_callback)
|
|
387
|
+
self.engine.send_is_alive(self.device, dev_types=[DB_DEV_TYPE])
|
|
388
|
+
await wait_for_event(self.db_server_found, timeout=0.3)
|
|
389
|
+
self.engine.unsubscribe(self.find_db_callback)
|
|
390
|
+
|
|
391
|
+
def request_db_values(self, addr):
|
|
392
|
+
if self.db_server:
|
|
393
|
+
self.engine.send_request(self.device, [self.db_server, ], "get_keys_values", {'device': addr})
|
|
394
|
+
|
|
395
|
+
def request_db_devices(self, key, value):
|
|
396
|
+
if self.db_server:
|
|
397
|
+
self.engine.send_request(self.device, [self.db_server, ], "get_devices", {'key': key, 'value': value})
|
|
398
|
+
|
|
399
|
+
def is_db_reply(self, msg):
|
|
400
|
+
if match_dev_type(msg, DB_DEV_TYPE) and msg.is_reply() and self.device.address in msg.targets:
|
|
401
|
+
return True
|
|
402
|
+
return False
|
|
403
|
+
|
|
404
|
+
#####################################################
|
|
405
|
+
# start/stop/idle/error
|
|
406
|
+
#####################################################
|
|
407
|
+
async def wait_completed(self, timeout=0.5):
|
|
408
|
+
"""wait until exit event is set"""
|
|
409
|
+
await wait_for_event(self.exit_event, timeout=timeout)
|
|
410
|
+
|
|
411
|
+
def update_idle(self):
|
|
412
|
+
self.last_msg_time = now()
|
|
413
|
+
|
|
414
|
+
async def idle_detector(self):
|
|
415
|
+
while True:
|
|
416
|
+
await asyncio.sleep(0.1)
|
|
417
|
+
if now() - self.last_msg_time > 0.4:
|
|
418
|
+
break
|
|
419
|
+
self.quit()
|
|
420
|
+
|
|
421
|
+
def run_until_timeout(self, timeout=3):
|
|
422
|
+
""" run the engine until timeout """
|
|
423
|
+
self.engine.add_timer(self.quit, timeout)
|
|
424
|
+
self.engine.run()
|
|
425
|
+
|
|
426
|
+
def run_until_idle(self):
|
|
427
|
+
self.engine.new_task(self.idle_detector())
|
|
428
|
+
self.engine.run()
|
|
429
|
+
|
|
430
|
+
def run_forever(self):
|
|
431
|
+
self.engine.run()
|
|
432
|
+
|
|
433
|
+
def quit(self):
|
|
434
|
+
self.engine.shutdown()
|
|
435
|
+
print()
|
|
436
|
+
print(LINE)
|
|
437
|
+
print(f"Found devices: {len(self.devices)}")
|
|
438
|
+
if self.db_server:
|
|
439
|
+
print(f"Metadb server: {self.db_server}")
|
|
440
|
+
t = round(now() - self.start_time, 2)
|
|
441
|
+
print(f"Total runtime: {t}s")
|
|
442
|
+
print(LINE)
|
|
443
|
+
|
|
444
|
+
def error(self, error_msg):
|
|
445
|
+
print(f"error: {error_msg}")
|
|
446
|
+
self.parser.print_help()
|
|
447
|
+
exit(1)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def colorize(color, text):
|
|
451
|
+
return f"{color}{text}{style.RESET}"
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def now():
|
|
455
|
+
return time.time()
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def match_dev_type(msg, dev_type):
|
|
459
|
+
if dev_type == 'any.any':
|
|
460
|
+
return True
|
|
461
|
+
if dev_type.endswith('.any'):
|
|
462
|
+
subtype = msg.dev_type.split('.')[0] + '.any'
|
|
463
|
+
if subtype == dev_type:
|
|
464
|
+
return True
|
|
465
|
+
else:
|
|
466
|
+
if msg.dev_type == dev_type:
|
|
467
|
+
return True
|
|
468
|
+
return False
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def match_address(msg, addr):
|
|
472
|
+
if (msg.source == addr) or (addr in msg.targets):
|
|
473
|
+
return True
|
|
474
|
+
return False
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
async def wait_for_event(event, timeout):
|
|
478
|
+
""" wait for a given event or timeout"""
|
|
479
|
+
wait_task = asyncio.create_task(event.wait())
|
|
480
|
+
await asyncio.wait([wait_task], timeout=timeout)
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
#####################################################
|
|
484
|
+
# dumper
|
|
485
|
+
#####################################################
|
|
486
|
+
def dumper():
|
|
487
|
+
helper = ToolboxHelper()
|
|
488
|
+
helper.parser.add_option("-f", dest="filter_address", help="only show given address")
|
|
489
|
+
helper.parser.add_option("-t", dest="filter_type", help="only show given device type")
|
|
490
|
+
|
|
491
|
+
helper.parse()
|
|
492
|
+
target = helper.check_address(helper.options.filter_address)
|
|
493
|
+
dev_type = helper.check_devtype(helper.options.filter_type)
|
|
494
|
+
|
|
495
|
+
eng = helper.setup_engine()
|
|
496
|
+
|
|
497
|
+
async def dumper_callback(msg):
|
|
498
|
+
# filter by address or dev_type
|
|
499
|
+
if target and not match_address(msg, target):
|
|
500
|
+
return
|
|
501
|
+
if dev_type and not match_dev_type(msg, dev_type):
|
|
502
|
+
return
|
|
503
|
+
|
|
504
|
+
# dump message
|
|
505
|
+
helper.dump_msg(msg)
|
|
506
|
+
|
|
507
|
+
eng.subscribe(dumper_callback)
|
|
508
|
+
helper.run_forever()
|
|
509
|
+
|
|
510
|
+
#####################################################
|
|
511
|
+
# alive
|
|
512
|
+
#####################################################
|
|
513
|
+
def is_alive():
|
|
514
|
+
helper = ToolboxHelper()
|
|
515
|
+
helper.parser.add_option("-t", dest="filter_type", help="only show given device type")
|
|
516
|
+
helper.parse()
|
|
517
|
+
dev_type = helper.check_devtype(helper.options.filter_type)
|
|
518
|
+
color = not helper.options.no_color
|
|
519
|
+
|
|
520
|
+
(eng, dev) = helper.setup_basic()
|
|
521
|
+
|
|
522
|
+
async def alive_callback(msg):
|
|
523
|
+
if (msg.source == dev.address) or (msg.is_alive() is False):
|
|
524
|
+
return
|
|
525
|
+
if match_dev_type(msg, dev_type) is False:
|
|
526
|
+
return
|
|
527
|
+
|
|
528
|
+
# idle detectiong
|
|
529
|
+
helper.update_idle()
|
|
530
|
+
if helper.get_device(msg.source) is None:
|
|
531
|
+
helper.parse_msg(msg)
|
|
532
|
+
if color:
|
|
533
|
+
print(f"{colorize(Colors.ADDR,msg.source)}: {colorize(Colors.DEV_TYPE,msg.dev_type)}")
|
|
534
|
+
else:
|
|
535
|
+
print(f"{msg.source}: {msg.dev_type}")
|
|
536
|
+
|
|
537
|
+
def start():
|
|
538
|
+
print(LINE)
|
|
539
|
+
helper.request_is_alive(dev_type=dev_type)
|
|
540
|
+
|
|
541
|
+
eng.subscribe(alive_callback)
|
|
542
|
+
eng.on_start(start)
|
|
543
|
+
helper.run_until_idle()
|
|
544
|
+
|
|
545
|
+
#####################################################
|
|
546
|
+
# info
|
|
547
|
+
#####################################################
|
|
548
|
+
def info():
|
|
549
|
+
helper = ToolboxHelper()
|
|
550
|
+
# redefine parser to add usage
|
|
551
|
+
helper.setup_parser("Usage: %prog [options] device_address")
|
|
552
|
+
(_, args) = helper.parse()
|
|
553
|
+
# command line address
|
|
554
|
+
if len(args) != 1:
|
|
555
|
+
helper.error("empty address")
|
|
556
|
+
target = tools.get_uuid(args[0])
|
|
557
|
+
if target is None:
|
|
558
|
+
helper.error("Invalid address: %s" % args[0])
|
|
559
|
+
|
|
560
|
+
(eng, dev) = helper.setup_basic()
|
|
561
|
+
|
|
562
|
+
def ready_to_show(dev):
|
|
563
|
+
if dev and helper.is_ready(dev):
|
|
564
|
+
helper.display_device(dev)
|
|
565
|
+
helper.exit_event.set()
|
|
566
|
+
|
|
567
|
+
def info_callback(msg, addr=target):
|
|
568
|
+
# collecting description and attributes
|
|
569
|
+
if msg.source != addr:
|
|
570
|
+
return
|
|
571
|
+
found = helper.parse_msg(msg)
|
|
572
|
+
ready_to_show(found)
|
|
573
|
+
|
|
574
|
+
def query_db_callback(msg):
|
|
575
|
+
if helper.is_db_reply(msg):
|
|
576
|
+
found = helper.parse_db_msg(msg)
|
|
577
|
+
ready_to_show(found)
|
|
578
|
+
|
|
579
|
+
async def run():
|
|
580
|
+
await helper.find_db_server()
|
|
581
|
+
if helper.db_server:
|
|
582
|
+
helper.request_db_values(target)
|
|
583
|
+
eng.subscribe(query_db_callback)
|
|
584
|
+
helper.request_is_alive(addr=target)
|
|
585
|
+
helper.request_info(target)
|
|
586
|
+
await helper.wait_completed()
|
|
587
|
+
helper.quit()
|
|
588
|
+
|
|
589
|
+
eng.subscribe(info_callback)
|
|
590
|
+
eng.on_start(run)
|
|
591
|
+
helper.run_forever()
|
|
592
|
+
|
|
593
|
+
#####################################################
|
|
594
|
+
# walker
|
|
595
|
+
#####################################################
|
|
596
|
+
def walker():
|
|
597
|
+
helper = ToolboxHelper()
|
|
598
|
+
helper.parser.add_option("-t", dest="filter_type", help="only show given device type")
|
|
599
|
+
helper.parse()
|
|
600
|
+
dev_type = helper.check_devtype(helper.options.filter_type)
|
|
601
|
+
(eng, dev) = helper.setup_basic()
|
|
602
|
+
|
|
603
|
+
def ready_to_show(dev):
|
|
604
|
+
if dev and helper.is_ready(dev):
|
|
605
|
+
helper.display_device(dev)
|
|
606
|
+
|
|
607
|
+
async def walker_callback(msg):
|
|
608
|
+
if msg.source == dev.address:
|
|
609
|
+
return
|
|
610
|
+
if match_dev_type(msg, DB_DEV_TYPE):
|
|
611
|
+
return
|
|
612
|
+
if match_dev_type(msg, dev_type) is False:
|
|
613
|
+
return
|
|
614
|
+
|
|
615
|
+
found = helper.parse_msg(msg)
|
|
616
|
+
helper.update_idle()
|
|
617
|
+
if msg.is_alive():
|
|
618
|
+
if not found.ready():
|
|
619
|
+
helper.request_info(msg.source)
|
|
620
|
+
helper.request_db_values(msg.source)
|
|
621
|
+
ready_to_show(found)
|
|
622
|
+
|
|
623
|
+
async def query_db_callback(msg):
|
|
624
|
+
if helper.is_db_reply(msg):
|
|
625
|
+
helper.update_idle()
|
|
626
|
+
found = helper.parse_db_msg(msg)
|
|
627
|
+
ready_to_show(found)
|
|
628
|
+
|
|
629
|
+
async def start():
|
|
630
|
+
await helper.find_db_server()
|
|
631
|
+
helper.update_idle()
|
|
632
|
+
if helper.db_server:
|
|
633
|
+
eng.subscribe(query_db_callback)
|
|
634
|
+
eng.subscribe(walker_callback)
|
|
635
|
+
helper.request_is_alive(dev_type=dev_type)
|
|
636
|
+
|
|
637
|
+
eng.on_start(start)
|
|
638
|
+
helper.run_until_idle()
|
|
639
|
+
|
|
640
|
+
#####################################################
|
|
641
|
+
# log
|
|
642
|
+
#####################################################
|
|
643
|
+
def log():
|
|
644
|
+
helper = ToolboxHelper()
|
|
645
|
+
helper.parser.add_option("-f", dest="filter_address", help="only show given address")
|
|
646
|
+
helper.parser.add_option("-t", dest="filter_type", help="only show given device type")
|
|
647
|
+
helper.parse()
|
|
648
|
+
|
|
649
|
+
target = helper.check_address(helper.options.filter_address)
|
|
650
|
+
dev_type = helper.check_devtype(helper.options.filter_type)
|
|
651
|
+
color = not helper.options.no_color
|
|
652
|
+
|
|
653
|
+
eng = helper.setup_engine()
|
|
654
|
+
|
|
655
|
+
def log_callback(msg):
|
|
656
|
+
if msg.is_alive() or (msg.action in HIDE_ACTION):
|
|
657
|
+
return
|
|
658
|
+
if target and not match_address(msg, target):
|
|
659
|
+
return
|
|
660
|
+
if dev_type and not match_dev_type(msg, dev_type):
|
|
661
|
+
return
|
|
662
|
+
|
|
663
|
+
color_value = Colors.DEFAULT
|
|
664
|
+
if msg.is_attributes_change():
|
|
665
|
+
color_value = Colors.ATTRIBUTS
|
|
666
|
+
elif msg.is_notify():
|
|
667
|
+
color_value = Colors.NOTIFY
|
|
668
|
+
elif msg.is_request():
|
|
669
|
+
color_value = Colors.REQUEST
|
|
670
|
+
elif msg.is_reply():
|
|
671
|
+
color_value = Colors.REPLY
|
|
672
|
+
|
|
673
|
+
if color:
|
|
674
|
+
dump = f"{Colors.DEFAULT}{time.ctime()} {Colors.ADDR}{msg.source} {Colors.DEV_TYPE}{msg.dev_type}\t{color_value}{msg.action} {msg.body}{style.RESET}"
|
|
675
|
+
else:
|
|
676
|
+
dump = f"{time.ctime()} {msg.source} {msg.dev_type}\t{msg.action} {msg.body}"
|
|
677
|
+
print(dump)
|
|
678
|
+
|
|
679
|
+
eng.subscribe(log_callback)
|
|
680
|
+
helper.run_forever()
|
|
681
|
+
|
|
682
|
+
#####################################################
|
|
683
|
+
# query db
|
|
684
|
+
#####################################################
|
|
685
|
+
def query_db():
|
|
686
|
+
helper = ToolboxHelper()
|
|
687
|
+
helper.parser.add_option("-d", dest="device_address", help="search by device address")
|
|
688
|
+
helper.parser.add_option("-k", dest="key", help="search by key")
|
|
689
|
+
helper.parser.add_option("-v", dest="value", help="search by value")
|
|
690
|
+
helper.parse()
|
|
691
|
+
# command line parsing
|
|
692
|
+
target = helper.check_address(helper.options.device_address)
|
|
693
|
+
key = helper.options.key
|
|
694
|
+
value = helper.options.value
|
|
695
|
+
color = not helper.options.no_color
|
|
696
|
+
|
|
697
|
+
if target and key:
|
|
698
|
+
helper.error("-d and -k are exclusive")
|
|
699
|
+
|
|
700
|
+
if not (target or key):
|
|
701
|
+
helper.error("-d or -k is required")
|
|
702
|
+
|
|
703
|
+
if value and not key:
|
|
704
|
+
helper.error("-v requires -k")
|
|
705
|
+
|
|
706
|
+
(eng, dev) = helper.setup_basic()
|
|
707
|
+
|
|
708
|
+
def device_callback(msg):
|
|
709
|
+
# search by device address
|
|
710
|
+
if helper.is_db_reply(msg):
|
|
711
|
+
found = msg.body.get('device', None)
|
|
712
|
+
found_map = msg.body.get('map', None)
|
|
713
|
+
if found == target:
|
|
714
|
+
r = []
|
|
715
|
+
r.append(['Metadata', ''])
|
|
716
|
+
r.append(['========', ''])
|
|
717
|
+
if color:
|
|
718
|
+
r.append(['Server:', colorize(Colors.ADDR, helper.db_server)])
|
|
719
|
+
else:
|
|
720
|
+
r.append(['Server:', helper.db_server])
|
|
721
|
+
for k, v in found_map.items():
|
|
722
|
+
if color:
|
|
723
|
+
v = colorize(Colors.DB, v)
|
|
724
|
+
r.append([k, v])
|
|
725
|
+
print(tabulate(r, tablefmt=TABLE_STYLE))
|
|
726
|
+
helper.exit_event.set()
|
|
727
|
+
|
|
728
|
+
def key_callback(msg):
|
|
729
|
+
# search by key / value
|
|
730
|
+
if helper.is_db_reply(msg):
|
|
731
|
+
k = msg.body.get('key', None)
|
|
732
|
+
v = msg.body.get('value', None)
|
|
733
|
+
print(LINE)
|
|
734
|
+
print(f"Search result for key={k} value={v}")
|
|
735
|
+
print(LINE)
|
|
736
|
+
devs = msg.body.get('devices', [])
|
|
737
|
+
for dev in devs:
|
|
738
|
+
if color:
|
|
739
|
+
print(f"- {colorize(Colors.ADDR,dev)}")
|
|
740
|
+
else:
|
|
741
|
+
print(f"- {dev}")
|
|
742
|
+
print(f"\n# Found {len(devs)} devices")
|
|
743
|
+
helper.exit_event.set()
|
|
744
|
+
|
|
745
|
+
async def run():
|
|
746
|
+
await helper.find_db_server()
|
|
747
|
+
if helper.db_server:
|
|
748
|
+
# found db server, send request and wait to complete
|
|
749
|
+
if target:
|
|
750
|
+
eng.subscribe(device_callback)
|
|
751
|
+
helper.request_db_values(target)
|
|
752
|
+
await helper.wait_completed()
|
|
753
|
+
elif key:
|
|
754
|
+
eng.subscribe(key_callback)
|
|
755
|
+
helper.request_db_devices(key, value)
|
|
756
|
+
await helper.wait_completed()
|
|
757
|
+
else:
|
|
758
|
+
print("\nNo metadata server found")
|
|
759
|
+
helper.quit()
|
|
760
|
+
|
|
761
|
+
eng.on_start(run)
|
|
762
|
+
helper.run_forever()
|
|
763
|
+
|
|
764
|
+
#####################################################
|
|
765
|
+
# cleanup db
|
|
766
|
+
#####################################################
|
|
767
|
+
def clean_db():
|
|
768
|
+
helper = ToolboxHelper()
|
|
769
|
+
helper.parse()
|
|
770
|
+
|
|
771
|
+
(eng, dev) = helper.setup_basic()
|
|
772
|
+
db_devices = []
|
|
773
|
+
|
|
774
|
+
def alive_callback(msg):
|
|
775
|
+
if (msg.source == dev.address) or (msg.is_alive() is False):
|
|
776
|
+
return
|
|
777
|
+
if helper.get_device(msg.source) is None:
|
|
778
|
+
helper.parse_msg(msg)
|
|
779
|
+
# print(msg)
|
|
780
|
+
|
|
781
|
+
def devices_callback(msg):
|
|
782
|
+
if helper.is_db_reply(msg) and msg.action == 'get_devices':
|
|
783
|
+
r = msg.body.get('devices', [])
|
|
784
|
+
for k in r:
|
|
785
|
+
db_devices.append(k)
|
|
786
|
+
|
|
787
|
+
async def gather():
|
|
788
|
+
print("Gathering devices infos...")
|
|
789
|
+
await asyncio.sleep(2)
|
|
790
|
+
print("Done..")
|
|
791
|
+
missing = []
|
|
792
|
+
for k in db_devices:
|
|
793
|
+
dev = helper.get_device(tools.get_uuid(k))
|
|
794
|
+
if not dev:
|
|
795
|
+
missing.append(k)
|
|
796
|
+
for k in missing:
|
|
797
|
+
drop = input(f"Drop {k} from db server ? [Y|*]")
|
|
798
|
+
if drop == 'Y':
|
|
799
|
+
body = {"device": k, "map": None}
|
|
800
|
+
helper.request_action(helper.db_server, "update_keys_values", body)
|
|
801
|
+
await asyncio.sleep(0)
|
|
802
|
+
|
|
803
|
+
async def start():
|
|
804
|
+
await helper.find_db_server()
|
|
805
|
+
if helper.db_server:
|
|
806
|
+
eng.subscribe(alive_callback)
|
|
807
|
+
eng.subscribe(devices_callback)
|
|
808
|
+
helper.request_is_alive()
|
|
809
|
+
helper.request_db_devices(None, None)
|
|
810
|
+
await gather()
|
|
811
|
+
helper.quit()
|
|
812
|
+
|
|
813
|
+
else:
|
|
814
|
+
print("\nNo metadata server found")
|
|
815
|
+
# helper.quit()
|
|
816
|
+
|
|
817
|
+
eng.on_start(start)
|
|
818
|
+
helper.run_forever()
|
|
819
|
+
# helper.run_until_idle()
|
|
820
|
+
|
|
821
|
+
#####################################################
|
|
822
|
+
# send request
|
|
823
|
+
#####################################################
|
|
824
|
+
def send():
|
|
825
|
+
helper = ToolboxHelper()
|
|
826
|
+
helper.parser.add_option("-d", dest="target_address", help="target device address")
|
|
827
|
+
helper.parser.add_option("-r", dest="request_action", help="request action")
|
|
828
|
+
helper.parser.add_option("-b", dest="request_body", help="request body example: 'smooth:10 speed:20'")
|
|
829
|
+
helper.parse()
|
|
830
|
+
|
|
831
|
+
# cmd line parsing
|
|
832
|
+
target = helper.check_address(helper.options.target_address)
|
|
833
|
+
action = helper.options.request_action
|
|
834
|
+
if not (target and action):
|
|
835
|
+
helper.error("-d and -r are mandatory")
|
|
836
|
+
# body parsing
|
|
837
|
+
tmp = helper.options.request_body
|
|
838
|
+
body = None
|
|
839
|
+
if tmp:
|
|
840
|
+
body = {}
|
|
841
|
+
for k in tmp.split(' '):
|
|
842
|
+
(i, j) = k.split(':')
|
|
843
|
+
body[i] = j
|
|
844
|
+
# let's go
|
|
845
|
+
(eng, dev) = helper.setup_basic()
|
|
846
|
+
|
|
847
|
+
def action_callback(msg):
|
|
848
|
+
if msg.is_alive():
|
|
849
|
+
return
|
|
850
|
+
if msg.source == target:
|
|
851
|
+
helper.dump_msg(msg)
|
|
852
|
+
helper.exit_event.set()
|
|
853
|
+
|
|
854
|
+
async def run():
|
|
855
|
+
helper.request_action(target, action, body)
|
|
856
|
+
# wait at least 1 msg
|
|
857
|
+
await helper.wait_completed(2)
|
|
858
|
+
helper.quit()
|
|
859
|
+
|
|
860
|
+
eng.subscribe(action_callback)
|
|
861
|
+
eng.on_start(run)
|
|
862
|
+
helper.run_forever()
|
|
863
|
+
|
|
864
|
+
#####################################################
|
|
865
|
+
# tail msg
|
|
866
|
+
#####################################################
|
|
867
|
+
def tail():
|
|
868
|
+
helper = ToolboxHelper()
|
|
869
|
+
helper.parser.add_option("-f", dest="filter_address", help="only show given address")
|
|
870
|
+
helper.parser.add_option("-t", dest="filter_type", help="only show given device type")
|
|
871
|
+
helper.parser.add_option("-m", dest="filter_mode", help="hide some messages:\n 1=alives, 2=core actions, 3=replies, 4=all except notif")
|
|
872
|
+
|
|
873
|
+
helper.parse()
|
|
874
|
+
target = helper.check_address(helper.options.filter_address)
|
|
875
|
+
dev_type = helper.check_devtype(helper.options.filter_type)
|
|
876
|
+
mode = helper.options.filter_mode
|
|
877
|
+
if mode:
|
|
878
|
+
try:
|
|
879
|
+
mode = int(mode)
|
|
880
|
+
except ValueError:
|
|
881
|
+
helper.error("-m must be an integer")
|
|
882
|
+
if mode not in range(1, 5):
|
|
883
|
+
helper.error("-m must be in range 1-4")
|
|
884
|
+
else:
|
|
885
|
+
mode = 0
|
|
886
|
+
color = not helper.options.no_color
|
|
887
|
+
eng = helper.setup_engine()
|
|
888
|
+
|
|
889
|
+
def tail_callback(msg):
|
|
890
|
+
if mode > 0 and msg.is_alive():
|
|
891
|
+
return
|
|
892
|
+
if target and not match_address(msg, target):
|
|
893
|
+
return
|
|
894
|
+
if dev_type and not match_dev_type(msg, dev_type):
|
|
895
|
+
return
|
|
896
|
+
if mode > 1 and msg.action in HIDE_ACTION:
|
|
897
|
+
return
|
|
898
|
+
if mode > 2 and msg.is_reply():
|
|
899
|
+
return
|
|
900
|
+
if mode > 3 and msg.is_request():
|
|
901
|
+
return
|
|
902
|
+
|
|
903
|
+
color_value = helper.color_for_msg(msg)
|
|
904
|
+
schem = '*'
|
|
905
|
+
if msg.msg_type == MessageType.REQUEST.value:
|
|
906
|
+
schem = '>'
|
|
907
|
+
elif msg.msg_type == MessageType.REPLY.value:
|
|
908
|
+
schem = '<'
|
|
909
|
+
elif msg.msg_type == MessageType.NOTIFY.value:
|
|
910
|
+
schem = '='
|
|
911
|
+
|
|
912
|
+
targets = [tools.reduce_addr(addr) for addr in msg.targets]
|
|
913
|
+
tmp = shutil.get_terminal_size()[0] - (2 + 18 + 36 + 20 + 16 + 7)
|
|
914
|
+
if tmp < 22:
|
|
915
|
+
tmp = 22
|
|
916
|
+
BODY_FORMAT = '%-22.'+str(tmp)+'s'
|
|
917
|
+
FORMAT = '%-2.02s %-18.18s %-36.36s (%-20.20s) %-16.16s '+BODY_FORMAT
|
|
918
|
+
res = FORMAT % (schem, msg.action, msg.source, msg.dev_type, targets, msg.body)
|
|
919
|
+
if color:
|
|
920
|
+
print(colorize(color_value, res))
|
|
921
|
+
else:
|
|
922
|
+
print(res)
|
|
923
|
+
|
|
924
|
+
# FIXME: find a better way to do this
|
|
925
|
+
print('\x1bc', end='') # clear screen
|
|
926
|
+
FORMAT = '%-2.02s %-18.18s %-36.36s (%-20.20s) %-16.16s %-22.22s'
|
|
927
|
+
print(FORMAT % ('=', 'action', 'source', 'dev_type', 'targets', 'body'))
|
|
928
|
+
|
|
929
|
+
eng.subscribe(tail_callback)
|
|
930
|
+
helper.run_forever()
|
|
931
|
+
|
|
932
|
+
#####################################################
|
|
933
|
+
# run pkg
|
|
934
|
+
#####################################################
|
|
935
|
+
def pkgrun():
|
|
936
|
+
helper = ToolboxHelper()
|
|
937
|
+
# redefine parser to add usage
|
|
938
|
+
helper.setup_parser("Usage: %prog [options] pkg1 pkg2 ...")
|
|
939
|
+
(_, args) = helper.parse()
|
|
940
|
+
eng = helper.setup_engine()
|
|
941
|
+
logger = logging.getLogger(helper.name)
|
|
942
|
+
|
|
943
|
+
def load_pkgs():
|
|
944
|
+
for k in args:
|
|
945
|
+
xaal_mod = 'xaal.' + k
|
|
946
|
+
try:
|
|
947
|
+
mod = importlib.import_module(xaal_mod)
|
|
948
|
+
except ModuleNotFoundError:
|
|
949
|
+
logger.critical("Unable to load module: %s" % xaal_mod)
|
|
950
|
+
continue
|
|
951
|
+
|
|
952
|
+
if hasattr(mod, 'setup') is False:
|
|
953
|
+
logger.critical("Unable to find setup %s" % xaal_mod)
|
|
954
|
+
continue
|
|
955
|
+
logger.info(f"{xaal_mod} loaded")
|
|
956
|
+
result = mod.setup(eng)
|
|
957
|
+
if result is not True:
|
|
958
|
+
logger.critical("something goes wrong with package: %s" % xaal_mod)
|
|
959
|
+
|
|
960
|
+
load_pkgs()
|
|
961
|
+
helper.run_forever()
|
|
962
|
+
|
|
963
|
+
#####################################################
|
|
964
|
+
# ipdb shell on steroid
|
|
965
|
+
#####################################################
|
|
966
|
+
def shell():
|
|
967
|
+
helper = ToolboxHelper()
|
|
968
|
+
helper.parse()
|
|
969
|
+
eng = helper.setup_engine()
|
|
970
|
+
eng.disable_msg_filter()
|
|
971
|
+
|
|
972
|
+
# load IPython
|
|
973
|
+
try:
|
|
974
|
+
import IPython
|
|
975
|
+
from IPython.lib import backgroundjobs
|
|
976
|
+
except ModuleNotFoundError:
|
|
977
|
+
print("Error: Unable to load IPython\n")
|
|
978
|
+
print("Please install IPython to use xAAL shell")
|
|
979
|
+
print("$ pip install ipython\n")
|
|
980
|
+
exit(1)
|
|
981
|
+
|
|
982
|
+
logging.getLogger("parso").setLevel(logging.WARNING)
|
|
983
|
+
logging.getLogger("blib2to3").setLevel(logging.WARNING)
|
|
984
|
+
|
|
985
|
+
# run the engine in background
|
|
986
|
+
jobs = backgroundjobs.BackgroundJobManager()
|
|
987
|
+
jobs.new(eng.run)
|
|
988
|
+
|
|
989
|
+
# imported modules for convenient use in the shell
|
|
990
|
+
from xaal.schemas import devices
|
|
991
|
+
from xaal.lib import Message, Attribute, Device
|
|
992
|
+
from xaal.monitor import Monitor
|
|
993
|
+
|
|
994
|
+
IPython.embed(banner1="============================== xAAL Shell ==============================",
|
|
995
|
+
banner2=f"* AsyncEngine running in background:\n* eng = {eng}\n\n",
|
|
996
|
+
colors="Linux",
|
|
997
|
+
confirm_exit=False,
|
|
998
|
+
separate_in='',
|
|
999
|
+
autoawait=True)
|
|
1000
|
+
|
|
1001
|
+
print("* Ending Engine")
|
|
1002
|
+
eng.shutdown()
|
|
1003
|
+
print("* Bye bye")
|
|
1004
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from xaal.lib import tools
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def main():
|
|
6
|
+
uuid = None
|
|
7
|
+
if len(sys.argv) > 1:
|
|
8
|
+
value = sys.argv[1]
|
|
9
|
+
uuid = tools.get_uuid(value)
|
|
10
|
+
if uuid == None:
|
|
11
|
+
uuid=tools.get_random_uuid()
|
|
12
|
+
print("TXT: %s" % uuid)
|
|
13
|
+
|
|
14
|
+
print("HEX: ",end="")
|
|
15
|
+
for b in uuid.bytes:
|
|
16
|
+
print("0x%x" % b,end=',')
|
|
17
|
+
print()
|
|
18
|
+
|
|
19
|
+
print("INT: ",end="")
|
|
20
|
+
for b in uuid.bytes:
|
|
21
|
+
print("%d" % b,end=',')
|
|
22
|
+
print()
|
|
23
|
+
|
|
24
|
+
if __name__ == '__main__':
|
|
25
|
+
main()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: xaal.tools
|
|
3
|
+
Version: 0.4
|
|
4
|
+
Summary: xAAL devices tools
|
|
5
|
+
Author-email: Jerome Kerdreux <Jerome.Kerdreux@imt-atlantique.fr>
|
|
6
|
+
License: GPL License
|
|
7
|
+
Keywords: xaal,tools
|
|
8
|
+
Classifier: Programming Language :: Python
|
|
9
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
10
|
+
Description-Content-Type: text/x-rst
|
|
11
|
+
Requires-Dist: xaal.lib
|
|
12
|
+
Requires-Dist: colored==1.4.3
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.rst
|
|
2
|
+
pyproject.toml
|
|
3
|
+
xaal.tools.egg-info/PKG-INFO
|
|
4
|
+
xaal.tools.egg-info/SOURCES.txt
|
|
5
|
+
xaal.tools.egg-info/dependency_links.txt
|
|
6
|
+
xaal.tools.egg-info/entry_points.txt
|
|
7
|
+
xaal.tools.egg-info/requires.txt
|
|
8
|
+
xaal.tools.egg-info/top_level.txt
|
|
9
|
+
xaal/tools/__init__.py
|
|
10
|
+
xaal/tools/keygen.py
|
|
11
|
+
xaal/tools/toolbox.py
|
|
12
|
+
xaal/tools/uuidgen.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[console_scripts]
|
|
2
|
+
xaal-cleandb = xaal.tools.toolbox:clean_db
|
|
3
|
+
xaal-dumper = xaal.tools.toolbox:dumper
|
|
4
|
+
xaal-info = xaal.tools.toolbox:info
|
|
5
|
+
xaal-isalive = xaal.tools.toolbox:is_alive
|
|
6
|
+
xaal-keygen = xaal.tools.keygen:main
|
|
7
|
+
xaal-log = xaal.tools.toolbox:log
|
|
8
|
+
xaal-pkgrun = xaal.tools.toolbox:pkgrun
|
|
9
|
+
xaal-querydb = xaal.tools.toolbox:query_db
|
|
10
|
+
xaal-send = xaal.tools.toolbox:send
|
|
11
|
+
xaal-shell = xaal.tools.toolbox:shell
|
|
12
|
+
xaal-tail = xaal.tools.toolbox:tail
|
|
13
|
+
xaal-uuidgen = xaal.tools.uuidgen:main
|
|
14
|
+
xaal-walker = xaal.tools.toolbox:walker
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
xaal
|