mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-19 23:03:33 +08:00
pyuavcan docstrings
This commit is contained in:
@@ -2,4 +2,9 @@
|
|||||||
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||||
#
|
#
|
||||||
|
|
||||||
|
'''
|
||||||
|
Python UAVCAN package.
|
||||||
|
Currently it implements only a DSDL parser (refer to the submodule 'dsdl').
|
||||||
|
'''
|
||||||
|
|
||||||
from . import dsdl
|
from . import dsdl
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||||
#
|
#
|
||||||
|
|
||||||
|
'''
|
||||||
|
This module implements a fully compliant UAVCAN DSDL parser.
|
||||||
|
'''
|
||||||
|
|
||||||
from .parser import Parser, parse_namespaces, \
|
from .parser import Parser, parse_namespaces, \
|
||||||
Type, PrimitiveType, ArrayType, CompoundType, \
|
Type, PrimitiveType, ArrayType, CompoundType, \
|
||||||
Attribute, Field, Constant
|
Attribute, Field, Constant
|
||||||
|
|||||||
@@ -6,12 +6,19 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
class DsdlException(Exception):
|
class DsdlException(Exception):
|
||||||
|
'''
|
||||||
|
This exception is raised in case of a parser failure.
|
||||||
|
Fields:
|
||||||
|
file Source file path where the error has occurred. Optional, will be None if unknown.
|
||||||
|
line Source file line number where the error has occurred. Optional, will be None if unknown.
|
||||||
|
'''
|
||||||
def __init__(self, text, file=None, line=None):
|
def __init__(self, text, file=None, line=None):
|
||||||
Exception.__init__(self, text)
|
Exception.__init__(self, text)
|
||||||
self.file = file
|
self.file = file
|
||||||
self.line = line
|
self.line = line
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
'''Returns nicely formatted error string in GCC-like format (can be parsed by e.g. Eclipse error parser)'''
|
||||||
if self.file and self.line:
|
if self.file and self.line:
|
||||||
return '%s:%d: %s' % (pretty_filename(self.file), self.line, Exception.__str__(self))
|
return '%s:%d: %s' % (pretty_filename(self.file), self.line, Exception.__str__(self))
|
||||||
if self.file:
|
if self.file:
|
||||||
@@ -20,6 +27,7 @@ class DsdlException(Exception):
|
|||||||
|
|
||||||
|
|
||||||
def pretty_filename(filename):
|
def pretty_filename(filename):
|
||||||
|
'''Returns a nice human readable path to 'filename'.'''
|
||||||
a = os.path.abspath(filename)
|
a = os.path.abspath(filename)
|
||||||
r = os.path.relpath(filename)
|
r = os.path.relpath(filename)
|
||||||
return a if len(a) < len(r) else r
|
return a if len(a) < len(r) else r
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ DATA_TYPE_ID_MAX = 1023
|
|||||||
MAX_DATA_STRUCT_LEN_BYTES = 439
|
MAX_DATA_STRUCT_LEN_BYTES = 439
|
||||||
|
|
||||||
class Type:
|
class Type:
|
||||||
|
'''
|
||||||
|
Common type description. The specialized type description classes inherit from this one.
|
||||||
|
Fields:
|
||||||
|
full_name Full type name string, e.g. "uavcan.protocol.NodeStatus"
|
||||||
|
category Any CATEGORY_*
|
||||||
|
'''
|
||||||
CATEGORY_PRIMITIVE = 0
|
CATEGORY_PRIMITIVE = 0
|
||||||
CATEGORY_ARRAY = 1
|
CATEGORY_ARRAY = 1
|
||||||
CATEGORY_COMPOUND = 2
|
CATEGORY_COMPOUND = 2
|
||||||
@@ -40,6 +46,14 @@ class Type:
|
|||||||
__repr__ = __str__
|
__repr__ = __str__
|
||||||
|
|
||||||
class PrimitiveType(Type):
|
class PrimitiveType(Type):
|
||||||
|
'''
|
||||||
|
Primitive type description, e.g. bool or float16.
|
||||||
|
Fields:
|
||||||
|
kind Any KIND_*
|
||||||
|
bitlen Bit length, 1 to 64
|
||||||
|
cast_mode Any CAST_MODE_*
|
||||||
|
value_range Tuple containing min and max values: (min, max)
|
||||||
|
'''
|
||||||
KIND_BOOLEAN = 0
|
KIND_BOOLEAN = 0
|
||||||
KIND_UNSIGNED_INT = 1
|
KIND_UNSIGNED_INT = 1
|
||||||
KIND_SIGNED_INT = 2
|
KIND_SIGNED_INT = 2
|
||||||
@@ -61,6 +75,7 @@ class PrimitiveType(Type):
|
|||||||
}[self.kind](bitlen)
|
}[self.kind](bitlen)
|
||||||
|
|
||||||
def get_normalized_definition(self):
|
def get_normalized_definition(self):
|
||||||
|
'''Please refer to the specification for details about normalized definitions.'''
|
||||||
cast_mode = 'saturated' if self.cast_mode == PrimitiveType.CAST_MODE_SATURATED else 'truncated'
|
cast_mode = 'saturated' if self.cast_mode == PrimitiveType.CAST_MODE_SATURATED else 'truncated'
|
||||||
primary_type = {
|
primary_type = {
|
||||||
PrimitiveType.KIND_BOOLEAN: 'bool',
|
PrimitiveType.KIND_BOOLEAN: 'bool',
|
||||||
@@ -71,14 +86,23 @@ class PrimitiveType(Type):
|
|||||||
return cast_mode + ' ' + primary_type
|
return cast_mode + ' ' + primary_type
|
||||||
|
|
||||||
def validate_value_range(self, value):
|
def validate_value_range(self, value):
|
||||||
|
'''Checks value range, throws DsdlException if the value cannot be represented by this type.'''
|
||||||
low, high = self.value_range
|
low, high = self.value_range
|
||||||
if not low <= value <= high:
|
if not low <= value <= high:
|
||||||
error('Value [%s] is out of range %s', value, self.value_range)
|
error('Value [%s] is out of range %s', value, self.value_range)
|
||||||
|
|
||||||
def get_max_bitlen(self):
|
def get_max_bitlen(self):
|
||||||
|
'''Returns type bit length.'''
|
||||||
return self.bitlen
|
return self.bitlen
|
||||||
|
|
||||||
class ArrayType(Type):
|
class ArrayType(Type):
|
||||||
|
'''
|
||||||
|
Array type description, e.g. float32[8], uint12[<34].
|
||||||
|
Fields:
|
||||||
|
value_type Description of the array value type; the type of this field inherits Type, e.g. PrimitiveType
|
||||||
|
mode Any MODE_*
|
||||||
|
max_size Maximum number of elements in the array
|
||||||
|
'''
|
||||||
MODE_STATIC = 0
|
MODE_STATIC = 0
|
||||||
MODE_DYNAMIC = 1
|
MODE_DYNAMIC = 1
|
||||||
|
|
||||||
@@ -89,10 +113,12 @@ class ArrayType(Type):
|
|||||||
Type.__init__(self, self.get_normalized_definition(), Type.CATEGORY_ARRAY)
|
Type.__init__(self, self.get_normalized_definition(), Type.CATEGORY_ARRAY)
|
||||||
|
|
||||||
def get_normalized_definition(self):
|
def get_normalized_definition(self):
|
||||||
|
'''Please refer to the specification for details about normalized definitions.'''
|
||||||
typedef = self.value_type.get_normalized_definition()
|
typedef = self.value_type.get_normalized_definition()
|
||||||
return ('%s[<=%d]' if self.mode == ArrayType.MODE_DYNAMIC else '%s[%d]') % (typedef, self.max_size)
|
return ('%s[<=%d]' if self.mode == ArrayType.MODE_DYNAMIC else '%s[%d]') % (typedef, self.max_size)
|
||||||
|
|
||||||
def get_max_bitlen(self):
|
def get_max_bitlen(self):
|
||||||
|
'''Returns total maximum bit length of the array, including length field if applicable.'''
|
||||||
payload_max_bitlen = self.max_size * self.value_type.get_max_bitlen()
|
payload_max_bitlen = self.max_size * self.value_type.get_max_bitlen()
|
||||||
return {
|
return {
|
||||||
self.MODE_DYNAMIC: payload_max_bitlen + self.max_size.bit_length(),
|
self.MODE_DYNAMIC: payload_max_bitlen + self.max_size.bit_length(),
|
||||||
@@ -100,6 +126,31 @@ class ArrayType(Type):
|
|||||||
}[self.mode]
|
}[self.mode]
|
||||||
|
|
||||||
class CompoundType(Type):
|
class CompoundType(Type):
|
||||||
|
'''
|
||||||
|
Compound type description, e.g. uavcan.protocol.NodeStatus.
|
||||||
|
Fields:
|
||||||
|
source_file Path to the DSDL definition file for this type
|
||||||
|
default_dtid Default Data Type ID, if specified, None otherwise
|
||||||
|
kind Any KIND_*
|
||||||
|
source_text Raw DSDL definition text (as is, with comments and the original formatting)
|
||||||
|
|
||||||
|
Fields if kind == KIND_SERVICE:
|
||||||
|
request_fields Request struct field list, the type of each element is Field
|
||||||
|
response_fields Response struct field list
|
||||||
|
request_constants Request struct constant list, the type of each element is Constant
|
||||||
|
response_constants Response struct constant list
|
||||||
|
|
||||||
|
Fields if kind == KIND_MESSAGE:
|
||||||
|
fields Field list, the type of each element is Field
|
||||||
|
constants Constant list, the type of each element is Constant
|
||||||
|
|
||||||
|
Extra methods if kind == KIND_SERVICE:
|
||||||
|
get_max_bitlen_request() Returns maximum total bit length for the serialized request struct
|
||||||
|
get_max_bitlen_response() Same for the response struct
|
||||||
|
|
||||||
|
Extra methods if kind == KIND_MESSAGE:
|
||||||
|
get_max_bitlen() Returns maximum total bit length for the serialized struct
|
||||||
|
'''
|
||||||
KIND_SERVICE = 0
|
KIND_SERVICE = 0
|
||||||
KIND_MESSAGE = 1
|
KIND_MESSAGE = 1
|
||||||
|
|
||||||
@@ -125,6 +176,10 @@ class CompoundType(Type):
|
|||||||
error('Compound type of unknown kind [%s]', kind)
|
error('Compound type of unknown kind [%s]', kind)
|
||||||
|
|
||||||
def get_dsdl_signature_source_definition(self):
|
def get_dsdl_signature_source_definition(self):
|
||||||
|
'''
|
||||||
|
Returns normalized DSDL definition text.
|
||||||
|
Please refer to the specification for details about normalized DSDL definitions.
|
||||||
|
'''
|
||||||
txt = StringIO()
|
txt = StringIO()
|
||||||
txt.write(self.full_name + '\n')
|
txt.write(self.full_name + '\n')
|
||||||
adjoin = lambda attrs: txt.write('\n'.join(x.get_normalized_definition() for x in attrs) + '\n')
|
adjoin = lambda attrs: txt.write('\n'.join(x.get_normalized_definition() for x in attrs) + '\n')
|
||||||
@@ -143,13 +198,24 @@ class CompoundType(Type):
|
|||||||
return txt.getvalue().strip().replace('\n\n\n', '\n').replace('\n\n', '\n')
|
return txt.getvalue().strip().replace('\n\n\n', '\n').replace('\n\n', '\n')
|
||||||
|
|
||||||
def get_dsdl_signature(self):
|
def get_dsdl_signature(self):
|
||||||
|
'''
|
||||||
|
Computes DSDL signature of this type.
|
||||||
|
Please refer to the specification for details about signatures.
|
||||||
|
'''
|
||||||
return compute_signature(self.get_dsdl_signature_source_definition())
|
return compute_signature(self.get_dsdl_signature_source_definition())
|
||||||
|
|
||||||
def get_normalized_definition(self):
|
def get_normalized_definition(self):
|
||||||
|
'''Returns full type name string, e.g. "uavcan.protocol.NodeStatus"'''
|
||||||
return self.full_name
|
return self.full_name
|
||||||
|
|
||||||
|
|
||||||
class Attribute:
|
class Attribute:
|
||||||
|
'''
|
||||||
|
Base class of an attribute description.
|
||||||
|
Fields:
|
||||||
|
type Attribute type description, the type of this field inherits the class Type, e.g. PrimitiveType
|
||||||
|
name Attribute name string
|
||||||
|
'''
|
||||||
def __init__(self, type, name): # @ReservedAssignment
|
def __init__(self, type, name): # @ReservedAssignment
|
||||||
self.type = type
|
self.type = type
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -160,10 +226,21 @@ class Attribute:
|
|||||||
__repr__ = __str__
|
__repr__ = __str__
|
||||||
|
|
||||||
class Field(Attribute):
|
class Field(Attribute):
|
||||||
|
'''
|
||||||
|
Field description.
|
||||||
|
Does not add new fields to Attribute.
|
||||||
|
'''
|
||||||
def get_normalized_definition(self):
|
def get_normalized_definition(self):
|
||||||
return '%s %s' % (self.type.get_normalized_definition(), self.name)
|
return '%s %s' % (self.type.get_normalized_definition(), self.name)
|
||||||
|
|
||||||
class Constant(Attribute):
|
class Constant(Attribute):
|
||||||
|
'''
|
||||||
|
Constant description.
|
||||||
|
Fields:
|
||||||
|
init_expression Constant initialization expression string, e.g. "2+2" or "'\x66'"
|
||||||
|
value Computed result of the initialization expression in the final type (e.g. int, float)
|
||||||
|
string_value Computed result of the initialization expression as string
|
||||||
|
'''
|
||||||
def __init__(self, type, name, init_expression, value): # @ReservedAssignment
|
def __init__(self, type, name, init_expression, value): # @ReservedAssignment
|
||||||
Attribute.__init__(self, type, name)
|
Attribute.__init__(self, type, name)
|
||||||
self.init_expression = init_expression
|
self.init_expression = init_expression
|
||||||
@@ -177,6 +254,9 @@ class Constant(Attribute):
|
|||||||
|
|
||||||
|
|
||||||
class Parser:
|
class Parser:
|
||||||
|
'''
|
||||||
|
DSDL parser logic. Do not use this class directly; use the helper function instead.
|
||||||
|
'''
|
||||||
LOGGER_NAME = 'dsdl_parser'
|
LOGGER_NAME = 'dsdl_parser'
|
||||||
|
|
||||||
def __init__(self, search_dirs):
|
def __init__(self, search_dirs):
|
||||||
@@ -500,6 +580,28 @@ def validate_data_struct_len(t):
|
|||||||
|
|
||||||
|
|
||||||
def parse_namespaces(source_dirs, search_dirs=None):
|
def parse_namespaces(source_dirs, search_dirs=None):
|
||||||
|
'''
|
||||||
|
Use only this function to parse DSDL definitions.
|
||||||
|
This function takes a list of root namespace directories (containing DSDL definition files to parse) and an
|
||||||
|
optional list of search directories (containing DSDL definition files that can be referenced from the types
|
||||||
|
that are going to be parsed).
|
||||||
|
Returns the list of parsed type definitions, where type of each element is CompoundType.
|
||||||
|
Args:
|
||||||
|
source_dirs List of root namespace directories to parse.
|
||||||
|
search_dirs List of root namespace directories with referenced types (optional). This list is
|
||||||
|
automaitcally extended with source_dirs.
|
||||||
|
Example:
|
||||||
|
>>> import pyuavcan
|
||||||
|
>>> a = pyuavcan.dsdl.parse_namespaces(['../dsdl/uavcan'])
|
||||||
|
>>> len(a)
|
||||||
|
77
|
||||||
|
>>> a[0]
|
||||||
|
uavcan.Timestamp
|
||||||
|
>>> a[0].fields
|
||||||
|
[truncated uint48 husec]
|
||||||
|
>>> a[0].constants
|
||||||
|
[saturated uint48 UNKNOWN = 0, saturated uint48 USEC_PER_LSB = 100]
|
||||||
|
'''
|
||||||
def walk():
|
def walk():
|
||||||
import fnmatch
|
import fnmatch
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|||||||
@@ -16,16 +16,23 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
|||||||
# Check: 0x62EC59E3F1A4F00A
|
# Check: 0x62EC59E3F1A4F00A
|
||||||
#
|
#
|
||||||
class Signature:
|
class Signature:
|
||||||
|
'''
|
||||||
|
This class implements the UAVCAN DSDL signature hash function. Please refer to the specification for details.
|
||||||
|
'''
|
||||||
MASK64 = 0xFFFFFFFFFFFFFFFF
|
MASK64 = 0xFFFFFFFFFFFFFFFF
|
||||||
POLY = 0x42F0E1EBA9EA3693
|
POLY = 0x42F0E1EBA9EA3693
|
||||||
|
|
||||||
def __init__(self, extend_from=None):
|
def __init__(self, extend_from=None):
|
||||||
|
'''
|
||||||
|
extend_from Initial value (optional)
|
||||||
|
'''
|
||||||
if extend_from is not None:
|
if extend_from is not None:
|
||||||
self._crc = (int(extend_from) & Signature.MASK64) ^ Signature.MASK64
|
self._crc = (int(extend_from) & Signature.MASK64) ^ Signature.MASK64
|
||||||
else:
|
else:
|
||||||
self._crc = Signature.MASK64
|
self._crc = Signature.MASK64
|
||||||
|
|
||||||
def add(self, data_bytes):
|
def add(self, data_bytes):
|
||||||
|
'''Feed ASCII string or bytes to the signature function'''
|
||||||
try:
|
try:
|
||||||
if isinstance(data_bytes, basestring): # Python 2.7 compatibility
|
if isinstance(data_bytes, basestring): # Python 2.7 compatibility
|
||||||
data_bytes = map(ord, data_bytes)
|
data_bytes = map(ord, data_bytes)
|
||||||
@@ -42,10 +49,15 @@ class Signature:
|
|||||||
self._crc <<= 1
|
self._crc <<= 1
|
||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
|
'''Returns integer signature value'''
|
||||||
return (self._crc & Signature.MASK64) ^ Signature.MASK64
|
return (self._crc & Signature.MASK64) ^ Signature.MASK64
|
||||||
|
|
||||||
|
|
||||||
def compute_signature(data):
|
def compute_signature(data):
|
||||||
|
'''
|
||||||
|
One-shot signature computation for ASCII string or bytes.
|
||||||
|
Returns integer signture value.
|
||||||
|
'''
|
||||||
s = Signature()
|
s = Signature()
|
||||||
s.add(data)
|
s.add(data)
|
||||||
return s.get_value()
|
return s.get_value()
|
||||||
|
|||||||
Reference in New Issue
Block a user