Skip to content

Commit 6c06d42

Browse files
author
JeromeGalan
authored
Merge pull request #176 from Luos-io/rc_2.2.9
Pyluos version 2.2.9
2 parents 2ea7dd5 + b1676a6 commit 6c06d42

File tree

8 files changed

+260
-4
lines changed

8 files changed

+260
-4
lines changed

.github/workflows/devpublish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
strategy:
1515
matrix:
16-
python-version: [3.6, 3.7, 3.8]
16+
python-version: [ '3.x' ]
1717

1818
steps:
1919
- uses: actions/checkout@v2

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
python-version: [3.6, 3.7, 3.8]
13+
python-version: [ '3.x' ]
1414

1515
steps:
1616
- uses: actions/checkout@v2

pyluos/device.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,13 @@ def __repr__(self):
8080
prefill = fill
8181
return s
8282

83+
8384
class Device(object):
8485
_heartbeat_timeout = 5 # in sec.
8586
_max_alias_length = 15
8687
_base_log_conf = os.path.join(os.path.dirname(__file__),
8788
'logging_conf.json')
89+
_freedomLink = None
8890

8991
def __init__(self, host,
9092
IO=None,
@@ -136,6 +138,10 @@ def close(self):
136138
print("Warning: device closed on timeout, background thread is still running.")
137139
self._io.close()
138140

141+
def link_to_freedomrobotics(self):
142+
from .integration.freedomRobotics import FreedomLink
143+
self._freedomLink = FreedomLink(self)
144+
139145
def pause(self):
140146
self._pause = True
141147
time.sleep(1)
@@ -256,19 +262,25 @@ def _update(self, new_state):
256262
alias = new_state['dead_service']
257263
if hasattr(self, alias):
258264
getattr(self, alias)._kill()
265+
if (self._freedomLink != None):
266+
self._freedomLink._kill(alias)
259267
if 'assert' in new_state.keys() :
260268
# A node assert, print assert informations
261269
if (('node_id' in new_state['assert']) and ('file' in new_state['assert']) and ('line' in new_state['assert'])):
262270
s = "************************* ASSERT *************************\n"
263271
s += "* Node " + str(new_state['assert']['node_id']) + " assert in file " + new_state['assert']['file'] + " line " + str(new_state['assert']['line'])
264272
s += "\n**********************************************************"
265273
print (s)
274+
if (self._freedomLink != None):
275+
self._freedomLink._assert(alias)
266276
if 'services' not in new_state.keys():
267277
return
268278

269279
for alias, mod in new_state['services'].items():
270280
if hasattr(self, alias):
271281
getattr(self, alias)._update(mod)
282+
if (self._freedomLink != None):
283+
self._freedomLink._update(alias, mod)
272284

273285
self._last_update = time.time()
274286

pyluos/integration/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

pyluos/integration/freedomRobotics.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
from freedomrobotics.link import Link
2+
3+
import logging
4+
import time
5+
6+
class FreedomLink(object):
7+
def __init__(self, device):
8+
self._link = Link("core", command_callback=self.callback)
9+
self._delegate = device
10+
11+
def callback(msg):
12+
print("I receive:" + str(msg) )
13+
self._link.log("info", "I heard " + str(msg) )
14+
# Topic represent services.
15+
if hasattr(self._delegate, msg["topic"][1:]):
16+
service = getattr(self._delegate, msg["topic"][1:])
17+
print ("we have this service")
18+
# We have this service.
19+
if hasattr(service, msg["message"]):
20+
service_data = getattr(service, msg["message"])
21+
service_data = msg["message"][msg["message"]]
22+
23+
def _update(self, alias, new_state):
24+
if 'io_state' in new_state:
25+
self._link.message("/" + alias + "/io_state", \
26+
"sensor_msgs/Joy", \
27+
{"io_state": new_state['io_state']})
28+
if 'temperature' in new_state:
29+
self._link.message("/" + alias + "/temperature", \
30+
"sensor_msgs/Temperature", \
31+
{"temperature": new_state['temperature']})
32+
if 'lux' in new_state:
33+
self._link.message("/" + alias + "/lux", \
34+
"sensor_msgs/Illuminance", \
35+
{"illuminance": new_state['lux']})
36+
if 'rot_position' in new_state:
37+
self._link.message("/" + alias + "/rot_position", \
38+
"sensor_msgs/JointState", \
39+
{"position": new_state['rot_position'], "name":alias})
40+
if 'trans_position' in new_state:
41+
self._link.message("/" + alias + "/trans_position", \
42+
"sensor_msgs/Range", \
43+
{"range": new_state['trans_position']})
44+
if 'rot_speed' in new_state:
45+
self._link.message("/" + alias + "/rot_speed", \
46+
"sensor_msgs/JointState", \
47+
{"velocity": new_state['rot_speed'], "name":alias})
48+
if 'trans_speed' in new_state:
49+
self._link.message("/" + alias + "/trans_speed", \
50+
"sensor_msgs/JointState", \
51+
{"velocity": new_state['trans_speed'], "name":alias})
52+
if 'force' in new_state:
53+
self._link.message("/" + alias + "/force", \
54+
"sensor_msgs/JointState", \
55+
{"effort": new_state['force'], "name":alias})
56+
if 'current' in new_state:
57+
self._link.message("/" + alias + "/current", \
58+
"sensor_msgs/BatteryState", \
59+
{"current": new_state['current']})
60+
if 'volt' in new_state:
61+
self._link.message("/" + alias + "/volt", \
62+
"sensor_msgs/BatteryState", \
63+
{"voltage": new_state['volt']})
64+
if 'quaternion' in new_state:
65+
self._link.message("/" + alias + "/quaternion", \
66+
"geometry_msgs/Quaternion", \
67+
{"y": new_state['quaternion'][0],"x": new_state['quaternion'][1],"z": new_state['quaternion'][2],"w": new_state['quaternion'][3]})
68+
if 'linear_accel' in new_state:
69+
self._link.message("/" + alias + "/linear_accel", \
70+
"geometry_msgs/Accel", \
71+
{"linear": {"y": new_state['linear_accel'][0],"x": new_state['linear_accel'][1],"z": new_state['linear_accel'][2]}})
72+
if 'accel' in new_state:
73+
self._link.message("/" + alias + "/accel", \
74+
"geometry_msgs/Accel", \
75+
{"angular": {"y": new_state['accel'][0],"x": new_state['accel'][1],"z": new_state['accel'][2]}})
76+
if 'gyro' in new_state:
77+
self._link.message("/" + alias + "/gyro", \
78+
"geometry_msgs/Vector3", \
79+
{"y": new_state['gyro'][0],"x": new_state['gyro'][1],"z": new_state['gyro'][2]})
80+
if 'euler' in new_state:
81+
self._link.message("/" + alias + "/euler", \
82+
"geometry_msgs/Vector3", \
83+
{"y": new_state['euler'][0],"x": new_state['euler'][1],"z": new_state['euler'][2]})
84+
if 'compass' in new_state:
85+
self._link.message("/" + alias + "/compass", \
86+
"geometry_msgs/Vector3", \
87+
{"y": new_state['compass'][0],"x": new_state['compass'][1],"z": new_state['compass'][2]})
88+
if 'gravity_vector' in new_state:
89+
self._link.message("/" + alias + "/gravity", \
90+
"geometry_msgs/Vector3", \
91+
{"y": new_state['gravity_vector'][0],"x": new_state['gravity_vector'][1],"z": new_state['gravity_vector'][2]})
92+
93+
# I don't know what to do with those ones :
94+
# if 'rotational_matrix' in new_state:
95+
# self._rotational_matrix = new_state['rotational_matrix']
96+
# if 'pedometer' in new_state:
97+
# self._pedometer = new_state['pedometer']
98+
# if 'walk_time' in new_state:
99+
# self._walk_time = new_state['walk_time']
100+
101+
# if 'heading' in new_state:
102+
# self._heading = new_state['heading']
103+
# if 'revision' in new_state:
104+
# self._firmware_revision = new_state['revision']
105+
# if 'luos_revision' in new_state:
106+
# self._luos_revision = new_state['luos_revision']
107+
# if 'luos_statistics' in new_state:
108+
# self._luos_statistics = new_state['luos_statistics']
109+
110+
111+
def _kill(self, alias):
112+
print ("service", alias, "have been excluded from the network due to no responses.")
113+
114+
def _assert(self, alias):
115+
print ("service", alias, "assert.")

pyluos/io/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ def is_ready(self):
1616
def read(self, trials=5):
1717
try:
1818
data = self.recv()
19-
return self.loads(data)
2019
except Exception as e:
2120
logging.getLogger(__name__).debug('Msg read failed: {}'.format(str(e)))
2221
if trials == 0:

pyluos/tools/bootloader.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,30 @@ def erase_flash(device, topic, nodes_to_program):
253253
print(u"\r\n ╰> Flash memory of node ", response['node'], " erased.")
254254
state = device._poll_once()
255255

256+
# retry sending failed messages
257+
for node in failed_nodes:
258+
send_node_command(device, node, topic, BOOTLOADER_ERASE)
259+
print(u"\r\n ╰> Retry erase memory of node ", node)
260+
init_time = time.time()
261+
state = device._poll_once()
262+
while len(failed_nodes):
263+
if(time.time() - init_time > ERASE_TIMEOUT):
264+
return_value = False
265+
break
266+
267+
# check if it is a response message
268+
if 'bootloader' in state:
269+
for response in state['bootloader']:
270+
if (response['response'] == BOOTLOADER_ERASE_RESP):
271+
# this node responded, delete it from the failed nodes list
272+
if response['node'] in failed_nodes:
273+
failed_nodes.remove(response['node'])
274+
print(u"\r\n ╰> Flash memory of node ", response['node'], " erased.")
275+
state = device._poll_once()
276+
277+
if not len(failed_nodes):
278+
return_value = True
279+
256280
waiting_bg.terminate()
257281
return return_value, failed_nodes
258282

@@ -358,6 +382,32 @@ def send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_prog
358382
failed_nodes.remove(response['node'])
359383
# wait for next message
360384
state = device._poll_once()
385+
386+
for node in failed_nodes:
387+
# retry sending failed messages
388+
send_data_node(device, node, BOOTLOADER_BIN_CHUNK, frame_size, data_bytes)
389+
state = device._poll_once()
390+
print(u"\r\n ╰> Retry sending binary message to node ", node)
391+
init_time = time.time()
392+
while len(failed_nodes):
393+
# check for timeout of nodes
394+
if(time.time() - init_time > PROGRAM_TIMEOUT):
395+
return_value = False
396+
break
397+
398+
# check if it is a response message
399+
if 'bootloader' in state:
400+
for response in state['bootloader']:
401+
if (response['response'] == BOOTLOADER_BIN_CHUNK_RESP):
402+
# the node responsed, remove it for fails list
403+
if response['node'] in failed_nodes:
404+
failed_nodes.remove(response['node'])
405+
# wait for next message
406+
state = device._poll_once()
407+
408+
if not len(failed_nodes):
409+
return_value = True
410+
361411
return return_value, failed_nodes
362412

363413
# *******************************************************************************
@@ -379,6 +429,25 @@ def send_data(device, topic, command, size, data):
379429
# send json command
380430
device._write(json.dumps(bootloader_cmd).encode() + '\n'.encode() + data)
381431

432+
# *******************************************************************************
433+
# @brief send binary data with a header to a specific node
434+
# @param
435+
# @return None
436+
# *******************************************************************************
437+
def send_data_node(device, node, command, size, data):
438+
# create a json file with the list of the nodes to program
439+
bootloader_cmd = {
440+
'bootloader': {
441+
'command': {
442+
'size': [size],
443+
'type': command,
444+
'node': node,
445+
},
446+
}
447+
}
448+
# send json command
449+
device._write(json.dumps(bootloader_cmd).encode() + '\n'.encode() + data)
450+
382451
# *******************************************************************************
383452
# @brief send the binary end command
384453
# @param
@@ -411,6 +480,31 @@ def send_binary_end(device, topic, nodes_to_program):
411480
failed_nodes.remove(response['node'])
412481
state = device._poll_once()
413482

483+
for node in failed_nodes:
484+
# retry sending failed messages
485+
send_node_command(device, node, topic, BOOTLOADER_BIN_END)
486+
print(u"\r\n ╰> Retry sending end message to node ", node)
487+
state = device._poll_once()
488+
init_time = time.time()
489+
while len(failed_nodes):
490+
# check for timeout of nodes
491+
if(time.time() - init_time > RESP_TIMEOUT):
492+
return_value = False
493+
break
494+
495+
# check if it is a response message
496+
if 'bootloader' in state:
497+
for response in state['bootloader']:
498+
if (response['response'] == BOOTLOADER_BIN_END_RESP):
499+
# the node responsed, remove it for fails list
500+
if response['node'] in failed_nodes:
501+
failed_nodes.remove(response['node'])
502+
# wait for next message
503+
state = device._poll_once()
504+
505+
if not len(failed_nodes):
506+
return_value = True
507+
414508
return return_value, failed_nodes
415509

416510
# *******************************************************************************
@@ -475,6 +569,41 @@ def check_crc(device, topic, nodes_to_program):
475569
return_value = False
476570
state = device._poll_once()
477571

572+
for node in failed_nodes:
573+
# retry sending failed messages
574+
send_node_command(device, node, topic, BOOTLOADER_CRC_TEST)
575+
print(u"\r\n ╰> Retry sending crc demand to node ", node)
576+
state = device._poll_once()
577+
init_time = time.time()
578+
while len(failed_nodes):
579+
# check for timeout of nodes
580+
if(time.time() - init_time > RESP_TIMEOUT):
581+
return_value = False
582+
break
583+
584+
# check if it is a response message
585+
if 'bootloader' in state:
586+
for response in state['bootloader']:
587+
if (response['response'] == BOOTLOADER_CRC_RESP):
588+
source_crc = int.from_bytes(compute_crc(), byteorder='big')
589+
node_crc = response['crc_value']
590+
node_id = response['node']
591+
# crc properly received
592+
if (source_crc == node_crc):
593+
print(u" ╰> CRC test for node", node_id, " : OK.")
594+
if node_id in failed_nodes:
595+
timeout -= RESP_TIMEOUT
596+
failed_nodes.remove(node_id)
597+
else:
598+
# not a good crc
599+
print(u" ╰> CRC test for node", node_id, ": NOK.")
600+
print(u" ╰> waited :", hex(source_crc), ", received :", hex(node_crc))
601+
return_value = False
602+
state = device._poll_once()
603+
604+
if not len(failed_nodes):
605+
return_value = True
606+
478607
return return_value, failed_nodes
479608

480609
# *******************************************************************************

pyluos/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version = '2.2.8'
1+
version = '2.2.9'

0 commit comments

Comments
 (0)