123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- #
- # Copyright (C) 2014-2015 UAVCAN Development Team <uavcan.org>
- #
- # This software is distributed under the terms of the MIT License.
- #
- # Author: Ben Dyer <ben_dyer@mac.com>
- # Pavel Kirienko <pavel.kirienko@zubax.com>
- #
- """
- Python UAVCAN package.
- Supported Python versions: 3.2+, 2.7.
- """
- from __future__ import division, absolute_import, print_function, unicode_literals
- import os
- import sys
- import struct
- import pkg_resources
- import time
- from logging import getLogger
- try:
- # noinspection PyStatementEffect
- time.monotonic # Works natively in Python 3.3+
- except AttributeError:
- try:
- # noinspection PyPackageRequirements,PyUnresolvedReferences
- import monotonic # 3rd party dependency for old versions @UnresolvedImport
- # monotonic_wrapper is a temporary work around for https://github.com/UAVCAN/pyuavcan/issues/22
- # monotonic_wrapper stops arguments being passed to monotonic.monotonic
- def monotonic_wrapper(*args, **kwargs):
- return monotonic.monotonic()
- time.monotonic = monotonic_wrapper
- except ImportError:
- time.monotonic = time.time # Last resort - using non-monotonic time; this is no good but oh well
- print('''The package 'monotonic' is not available, the library will use real time instead of monotonic time.
- This implies that the library may misbehave if system clock is adjusted while the library is running.
- In order to fix this problem, consider either option:
- 1. Switch to Python 3.
- 2. Install the missing package, e.g. using pip:
- pip install monotonic''', file=sys.stderr)
- class UAVCANException(Exception):
- pass
- from .version import __version__
- import uavcan.node as node
- from uavcan.node import make_node
- from uavcan.driver import make_driver
- import uavcan.dsdl as dsdl
- import uavcan.transport as transport
- from uavcan.transport import get_uavcan_data_type, \
- get_active_union_field, switch_union_field, is_union, \
- get_constants, get_fields, \
- is_request, is_response
- from uavcan.introspect import value_to_constant_name, to_yaml
- TRANSFER_PRIORITY_LOWEST = 31
- TRANSFER_PRIORITY_HIGHEST = 0
- logger = getLogger(__name__)
- class Module(object):
- pass
- class Namespace(object):
- """Provides a nice object-based way to look up UAVCAN data types."""
- def __init__(self):
- self.__namespaces = set()
- # noinspection PyProtectedMember
- def _path(self, attrpath):
- """Returns the namespace object at the given .-separated path,
- creating any namespaces in the path that don't already exist."""
- attr, _, subpath = attrpath.partition(".")
- if attr not in self.__dict__:
- self.__dict__[attr] = Namespace()
- self.__namespaces.add(attr)
- if subpath:
- return self.__dict__[attr]._path(subpath)
- else:
- return self.__dict__[attr]
- def _namespaces(self):
- """Returns the top-level namespaces in this object"""
- return set(self.__namespaces)
- MODULE = Module()
- DATATYPES = {}
- TYPENAMES = {}
- # noinspection PyProtectedMember
- def load_dsdl(*paths, **args):
- """
- Loads the DSDL files under the given directory/directories, and creates
- types for each of them in the current module's namespace.
- If the exclude_dist argument is not present, or False, the DSDL
- definitions installed with this package will be loaded first.
- Also adds entries for all datatype (ID, kind)s to the DATATYPES
- dictionary, which maps datatype (ID, kind)s to their respective type
- classes.
- """
- global DATATYPES, TYPENAMES
- paths = list(paths)
- # Try to prepend the built-in DSDL files
- # TODO: why do we need try/except here?
- # noinspection PyBroadException
- try:
- if not args.get("exclude_dist", None):
- dsdl_path = pkg_resources.resource_filename(__name__, "dsdl_files") # @UndefinedVariable
- paths = [os.path.join(dsdl_path, "uavcan")] + paths
- custom_path = os.path.join(os.path.expanduser("~"), "uavcan_vendor_specific_types")
- if os.path.isdir(custom_path):
- paths += [f for f in [os.path.join(custom_path, f) for f in os.listdir(custom_path)]
- if os.path.isdir(f)]
- except Exception:
- pass
- root_namespace = Namespace()
- dtypes = dsdl.parse_namespaces(paths)
- for dtype in dtypes:
- namespace, _, typename = dtype.full_name.rpartition(".")
- root_namespace._path(namespace).__dict__[typename] = dtype
- TYPENAMES[dtype.full_name] = dtype
- if dtype.default_dtid:
- DATATYPES[(dtype.default_dtid, dtype.kind)] = dtype
- # Add the base CRC to each data type capable of being transmitted
- dtype.base_crc = dsdl.crc16_from_bytes(struct.pack("<Q", dtype.get_data_type_signature()))
- logger.debug("DSDL Load {: >30} DTID: {: >4} base_crc:{: >8}"
- .format(typename, dtype.default_dtid, hex(dtype.base_crc)))
- def create_instance_closure(closure_type, _mode=None):
- # noinspection PyShadowingNames
- def create_instance(*args, **kwargs):
- if _mode:
- assert '_mode' not in kwargs, 'Mode cannot be supplied to service type instantiation helper'
- kwargs['_mode'] = _mode
- return transport.CompoundValue(closure_type, *args, **kwargs)
- return create_instance
- dtype._instantiate = create_instance_closure(dtype)
- if dtype.kind == dtype.KIND_SERVICE:
- dtype.Request = create_instance_closure(dtype, _mode='request')
- dtype.Response = create_instance_closure(dtype, _mode='response')
- namespace = root_namespace._path("uavcan")
- for top_namespace in namespace._namespaces():
- MODULE.__dict__[str(top_namespace)] = namespace.__dict__[top_namespace]
- MODULE.__dict__["thirdparty"] = Namespace()
- for ext_namespace in root_namespace._namespaces():
- if str(ext_namespace) != "uavcan":
- # noinspection PyUnresolvedReferences
- MODULE.thirdparty.__dict__[str(ext_namespace)] = root_namespace.__dict__[ext_namespace]
- __all__ = ["dsdl", "transport", "load_dsdl", "DATATYPES", "TYPENAMES"]
- # Hack to support dynamically-generated attributes at the top level of the
- # module. It doesn't feel right but it's recommended by Guido:
- # https://mail.python.org/pipermail/python-ideas/2012-May/014969.html
- MODULE.__dict__ = globals()
- MODULE._module = sys.modules[MODULE.__name__]
- MODULE._pmodule = MODULE
- sys.modules[MODULE.__name__] = MODULE
- # Completing package initialization with loading default DSDL definitions
- load_dsdl()
- # Importing modules that may be dependent on the standard DSDL types
- import uavcan.app as app
|