""" Provides functions for the discovery of ODrive devices """ import sys import time import json import usb.core import usb.util import serial import serial.tools.list_ports import odrive.util import odrive.usbbulk_transport import odrive.serial_transport import re import time import os import odrive.protocol import itertools import struct import functools def noprint(x): pass class SimpleDeviceProperty(property): """ Used internally by dynamically created objects to translate property assignments and fetches into endpoint operations on the object's associated channel """ def __init__(self, channel, id, type, struct_format, can_read, can_write): self._channel = channel self._id = id self._type = type self._struct_format = struct_format property.__init__(self, self.fget if can_read else None, self.fset if can_write else None) def fget(self, obj): size = struct.calcsize(self._struct_format) buffer = self._channel.remote_endpoint_operation(self._id, None, True, size) return struct.unpack(self._struct_format, buffer)[0] def fset(self, obj, value): value = self._type(value) buffer = struct.pack(self._struct_format, value) # TODO: Currenly we wait for an ack here. Settle on the default guarantee. self._channel.remote_endpoint_operation(self._id, buffer, True, 0) def call_remote_function(channel, trigger_id, arg_properties, *args): """ Used internally by the dynamically created objects to translate function calls into endpoint operations on the associated channel """ if (len(arg_properties) != len(args)): raise TypeError("expected {} arguments but have {}".format(len(arg_properties), len(args))) for i in range(len(args)): arg_properties[i].fset(None, args[i]) channel.remote_endpoint_operation(trigger_id, None, True, 0) def setattr_or_raise_if_undefined(self, name, value): """ If employed as an object's __setattr__ function, this function makes sure that an assignment to an undefined attribute doesn't create a new attribute but instead raises an exception """ # We can't use hasattr here because internally it fetches the property # value, creating unnecessary bus traffic if name in dir(self): object.__setattr__(self, name, value) else: raise TypeError('Cannot set name %r on object of type %s' % ( name, self.__class__.__name__)) def create_property(name, json_data, channel, printer): """ Dynamically creates a property based on a JSON definition """ name = name or "[anonymous]" type_str = json_data.get("type", None) if type_str is None: printer("property {} has no specified type".format(name)) return None if type_str == "float": property_type = float struct_format = "