Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ebff67b
Add brokers command line tools
nicolas-rabault Jan 9, 2024
2fd6bdf
switch to revision 3.1.0
nicolas-rabault Jan 9, 2024
a5600c2
Add a `map_custom_service` function allowing to manage custom pyluos …
nicolas-rabault Jan 26, 2024
4835292
Merge pull request #195 from Luos-io/feat/map
nicolas-rabault Jan 29, 2024
cfbb880
Improve refresh_freq calculation allowing multiple JSON per service p…
nicolas-rabault Mar 13, 2024
c7febbe
Adapt pyluos to the new Json format
nicolas-rabault Mar 22, 2024
cfe01b5
increase Serial default baudrate to 3000000 baud
nicolas-rabault Mar 22, 2024
937f7ff
Allow pyluos to receive multiple Json at once. It will be merged into…
nicolas-rabault Mar 25, 2024
9e64152
Merge pull request #197 from Luos-io/feat/refresh_freq
nicolas-rabault Jun 3, 2024
4e75582
remove pyluos limitation to send messages without receiving a gate me…
houkhouk May 30, 2024
584518b
Merge pull request #196 from houkhouk/fix/rem_pyluos_poll
nicolas-rabault Jun 19, 2024
7be2e53
fix pyluos limitation to send messages
Jun 19, 2024
ad423bc
modification of timeout value in websocket recv
Jun 20, 2024
3c5056f
Fix a missing import of time
nicolas-rabault Aug 1, 2024
111bdf8
Add torque management on servomotors
nicolas-rabault Aug 1, 2024
7b1ca02
Merge pull request #199 from Luos-io/feat/torque
nicolas-rabault Aug 14, 2024
8bb1cf0
Improve _last_update property
fleborne Nov 20, 2024
9bc12ae
Simplify code dealing with state reception
fleborne Nov 20, 2024
724eef2
Merge pull request #201 from fleborne/fix/last_update-management
nicolas-rabault Dec 3, 2024
9001272
Merge pull request #202 from fleborne/feat/improve-state-reception
nicolas-rabault Dec 3, 2024
044a1ae
Merge pull request #198 from BHAY-3DiTex/fix/polling
nicolas-rabault Dec 12, 2024
1259561
Remove sending of empty JSON to accelerate device setup
fleborne Dec 12, 2024
65d12be
Merge pull request #203 from fleborne/faster-device-setup
nicolas-rabault Dec 12, 2024
09b9974
Remove unused `test_mode` arg of Device
fleborne Feb 18, 2025
b6ce4d8
Add push_despite_empty_state to configure push_once behavior in devic…
fleborne Feb 18, 2025
f011d65
Remove push_despite_empty_state arg
fleborne Feb 18, 2025
148052d
Merge pull request #204 from fleborne/feat/push-despite-empty-state
nicolas-rabault Feb 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyluos/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging

from .device import Device
from .device import Device, map_custom_service
from .services import *

nh = logging.NullHandler()
Expand Down
52 changes: 31 additions & 21 deletions pyluos/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
def run_from_unittest():
return 'unittest' in sys.services

def map_custom_service(service_type, service_class):
name2mod[service_type] = service_class

class contList(list):
def __repr__(self):
Expand Down Expand Up @@ -85,7 +87,6 @@ class Device(object):
def __init__(self, host,
IO=None,
log_conf=_base_log_conf,
test_mode=False,
background_task=True,
*args, **kwargs):
if IO is not None:
Expand All @@ -109,7 +110,7 @@ def __init__(self, host,
self._setup()
self.logger.info('Device setup.')

self._last_update = time.time()
self._last_update = 0.0
self._running = True
self._pause = False

Expand Down Expand Up @@ -142,14 +143,12 @@ def play(self):
self._pause = False

def _setup(self):
self.logger.info('Sending detection signal.')
self._send({})
time.sleep(0.01)
startTime = time.time()
retry = 0
self.logger.info(f'Sending detection signal ({retry=})')
self._send({'detection': {}})
self.logger.info('Waiting for routing table...')
startTime = time.time()
state = self._poll_once()
retry = 0
while ('routing_table' not in state):
if ('route_table' in state):
self.logger.info("Watch out the Luos revision you are using on your board is too old to work with this revision of pyluos.\n Please consider updating Luos on your boards")
Expand All @@ -160,6 +159,7 @@ def _setup(self):
if retry > 5:
# detection is not working
sys.exit("Detection failed.")
self.logger.info(f'Sending detection signal ({retry=})')
self._send({'detection': {}})
startTime = time.time()
# Save routing table data
Expand All @@ -180,9 +180,15 @@ def _setup(self):
break
# create the node
self._nodes.append(AnyNode(id=node["node_id"], parent=parent_elem, connection=node["con"]))

filtered_services = contList([mod for mod in node["services"]
if 'type' in mod and mod['type'] in name2mod.keys()])
# list unrecognized services and print a warning
unrecognized_services = [mod for mod in node["services"]
if 'type' in mod and mod['type'] not in name2mod.keys()]
if (len(unrecognized_services) > 0):
self.logger.warning("Unrecognized services have been detected on node %d" % node["node_id"])
for mod in unrecognized_services:
self.logger.warning(" - service %s of type %s" % (mod['alias'], mod['type']))
# Create a list of services in the node
self._nodes[i].services = [
name2mod[mod['type']](id=mod['id'],
Expand Down Expand Up @@ -212,21 +218,25 @@ def services(self):
def nodes(self):
return nodeList(self._nodes)

@property
def last_update(self) -> float:
"""The last_update property."""
return self._last_update

# Poll state from hardware.
def _poll_once(self):
self._state = self._io.read()
if self._state != []:
self._state['timestamp'] = time.time()
return self._state
return []
state = self._io.read()
if state:
state['timestamp'] = time.time()
return state

def _poll_and_up(self):
while self._running:
if not self._pause:
state = self._poll_once()
if self._state != []:
if state:
self._update(state)
self._push_once()
self._push_once()
else:
time.sleep(0.1)

Expand Down Expand Up @@ -260,7 +270,7 @@ def _update(self, new_state):
self._freedomLink._kill(service.alias)
service._kill()
s += "\n* Service " + str(service.alias) + " have been excluded from the network due to no responses."

s += "\n*************************************************************"
print(s)
break
Expand All @@ -281,16 +291,16 @@ def _update(self, new_state):
break
if (self._freedomLink != None):
self._freedomLink._assert(alias)
if 'services' not in new_state.keys():
if 's' not in new_state.keys():
return

for alias, mod in new_state['services'].items():
for alias, mod in new_state['s'].items():
if hasattr(self, alias):
getattr(self, alias)._update(mod)
if (self._freedomLink != None):
self._freedomLink._update(alias, mod)

self._last_update = time.time()
self._last_update = float(new_state["timestamp"])

def update_cmd(self, alias, key, val):
with self._cmd_lock:
Expand All @@ -304,11 +314,11 @@ def update_data(self, alias, key, val, data):
def _push_once(self):
with self._cmd_lock:
if self._cmd:
self._write(json.dumps({'services': self._cmd}).encode())
self._write(json.dumps({'s': self._cmd}).encode())
self._cmd = defaultdict(lambda: defaultdict(lambda: None))
for cmd, binary in zip(self._cmd_data, self._binary):
time.sleep(0.01)
self._write(json.dumps({'services': cmd}).encode() + '\n'.encode() + binary)
self._write(json.dumps({'s': cmd}).encode() + '\n'.encode() + binary)

self._cmd_data = []
self._binary = []
Expand Down
13 changes: 12 additions & 1 deletion pyluos/io/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import logging
from mergedeep import merge, Strategy


class IOHandler(object):
Expand All @@ -16,7 +17,17 @@ def is_ready(self):
def read(self, trials=5):
try:
data = self.recv()
return self.loads(data)
if data is None:
return {}
table = data.splitlines()
if len(table) > 1:
# load the Json of each substring
jsn = [self.loads(sub_data) for sub_data in table]
# merge all the Json data
result = merge({}, *jsn, strategy=Strategy.ADDITIVE)
return result
else:
return self.loads(data)
except Exception as e:
logging.getLogger(__name__).debug('Msg read failed: {}'.format(str(e)))
if trials == 0:
Expand Down
4 changes: 2 additions & 2 deletions pyluos/io/serial_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def is_host_compatible(cls, host):

def __init__(self, host, baudrate=None):
if baudrate is None:
baudrate = os.getenv('LUOS_BAUDRATE', 1000000)
baudrate = os.getenv('LUOS_BAUDRATE', 3000000)

self._serial = _serial.Serial(host, baudrate)
self._serial.flush()
Expand Down Expand Up @@ -75,7 +75,7 @@ def write(self, data):

def close(self):
self._running = False
self._poll_loop.join()
self._poll_loop.join(timeout = 1)

self._serial.close()

Expand Down
6 changes: 4 additions & 2 deletions pyluos/io/ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def is_ready(self):

def recv(self):
try:
data = self._msg.get(True, 1)
data = self._msg.get(True, 0.01)
except queue.Empty:
data = None
return data
Expand All @@ -73,7 +73,7 @@ def write(self, data):

def close(self):
self._running = False
self._poll_loop.join()
self._poll_loop.join(timeout = 2)
self._ws.close()

def _poll(self):
Expand All @@ -94,6 +94,8 @@ def extract_line(s):

while self._running:
s = self._ws.recv()
if isinstance(s, str):
return
buff = buff + s
while self._running:
line, buff = extract_line(buff)
Expand Down
1 change: 1 addition & 0 deletions pyluos/services/load.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .service import Service
import time


class Load(Service):
Expand Down
25 changes: 17 additions & 8 deletions pyluos/services/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ def __init__(self,
self._luos_revision = "Unknown"
self._robus_revision = "Unknown"
self._killed = False
self._last_update = []
self._last_update = [time.time()]
self._tracked_property = ""
self._luos_statistics = {}

def __repr__(self):
Expand All @@ -56,13 +57,21 @@ def _update(self, new_state):
if not isinstance(new_state, dict):
new_state = {new_state: ""}

self._last_update.append(time.time())
if (len(self._last_update) > 1):
self.max_refresh_time = max(self.max_refresh_time, self._last_update[-1] - self._last_update[-2])
if (self._last_update[0] < time.time() - 1.0):
while (self._last_update[0] < time.time() - 10.0):
self._last_update.pop(0)
self.refresh_freq = (len(self._last_update) / 10.0) * 0.05 + 0.95 * self.refresh_freq
# Check if we alredy have a property to track or if we didn't receive any property since 2 seconds
if (self._tracked_property == "") or (self._last_update[-1] < time.time() - 2.0):
# the property we track is void or not available anymore, we have to get one of the property received.
for key in new_state.keys():
self._tracked_property = key
self._last_update.append(time.time())
break
elif (self._tracked_property in new_state.keys()):
self._last_update.append(time.time())
if (len(self._last_update) > 1):
self.max_refresh_time = max(self.max_refresh_time, self._last_update[-1] - self._last_update[-2])
if (self._last_update[0] < time.time() - 1.0):
while (self._last_update[0] < time.time() - 10.0):
self._last_update.pop(0)
self.refresh_freq = (len(self._last_update) / 10.0) * 0.05 + 0.95 * self.refresh_freq

if 'revision' in new_state.keys():
self._firmware_revision = new_state['revision']
Expand Down
30 changes: 30 additions & 0 deletions pyluos/services/servoMotor.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __init__(self, id, alias, device):
self._target_rot_position = 0.0
self._target_trans_speed = 0.0
self._target_trans_position = 0.0
self._target_torque = 0.0

# report modes
self._rot_position = 0.0
Expand Down Expand Up @@ -390,6 +391,35 @@ def target_trans_position(self, s):
else :
self._push_value("target_trans_position", s)

# torque
@property
def target_torque(self):
if (self._config[ServoMotor._MODE_TORQUE] != True):
print("torque mode could be not enabled in the service please use 'device.service.torque_mode = True' to enable it")
return self._target_torque

@target_torque.setter
def target_torque(self, s):
self._target_torque = s
self._push_value("target_torque", s)

@property
def torque_mode(self):
return self._config[ServoMotor._MODE_TORQUE]

@torque_mode.setter
def torque_mode(self, enable):
self._config[ServoMotor._MODE_TORQUE] = True if enable != 0 else False
if (enable == True) :
self._config[ServoMotor._MODE_LINEAR_POSITION] = False
self._config[ServoMotor._MODE_POWER] = False
self._config[ServoMotor._MODE_ANGULAR_POSITION] = False
self._config[ServoMotor._MODE_ANGULAR_SPEED ] = False
self._config[ServoMotor._MODE_LINEAR_SPEED] = False
self._push_value('parameters', self._convert_config())
time.sleep(0.01)


@property
def trans_position_mode(self):
return self._config[ServoMotor._MODE_LINEAR_POSITION]
Expand Down
Loading