mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-05-18 01:27:34 +08:00
DSDL compiler for libuavcan - dsdlc
This commit is contained in:
@@ -12,3 +12,6 @@ lib*.so.*
|
||||
.project
|
||||
.cproject
|
||||
.pydevproject
|
||||
|
||||
# libuavcan DSDL compiler default output directory
|
||||
*dsdlc_output
|
||||
|
||||
@@ -1 +1,182 @@
|
||||
|
||||
/*
|
||||
* UAVCAN data structure definition for libuavcan.
|
||||
*
|
||||
* Autogenerated, do not edit.
|
||||
*
|
||||
* Source file: ${t.filename}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <uavcan/data_type.hpp>
|
||||
#include <uavcan/global_data_type_registry.hpp>
|
||||
#include <uavcan/internal/util.hpp>
|
||||
|
||||
% for inc in t.cpp_includes:
|
||||
#include <${inc}>
|
||||
% endfor
|
||||
|
||||
#if !defined(UAVCAN_CONSTEXPR)
|
||||
# if __cplusplus < 201100L
|
||||
# define UAVCAN_CONSTEXPR const
|
||||
# else
|
||||
# define UAVCAN_CONSTEXPR constexpr
|
||||
# endif
|
||||
#endif
|
||||
|
||||
<%!
|
||||
indent = lambda text, idnt=' ': idnt + text.replace('\n', '\n' + idnt)
|
||||
%>
|
||||
|
||||
/******************************************************************************
|
||||
% for line in t.source_text.strip().splitlines():
|
||||
${line}
|
||||
% endfor
|
||||
******************************************************************************/
|
||||
|
||||
% for nsc in t.cpp_namespace_components:
|
||||
namespace ${nsc}
|
||||
{
|
||||
% endfor
|
||||
|
||||
struct ${t.short_name}
|
||||
{
|
||||
<%def name="generate_primary_body(type_name, max_bitlen, fields, constants)" buffered="True">
|
||||
typedef const ${type_name}& ParameterType;
|
||||
typedef ${type_name}& ReferenceType;
|
||||
|
||||
<%def name="expand_attr_types(group_name, attrs)">
|
||||
struct ${group_name}
|
||||
{
|
||||
% for a in attrs:
|
||||
typedef ${a.cpp_type} ${a.name};
|
||||
% endfor
|
||||
};
|
||||
</%def>
|
||||
${expand_attr_types('ConstantTypes', constants)}
|
||||
${expand_attr_types('FieldTypes', fields)}
|
||||
|
||||
<%def name="expand_enum_per_field(enum_name)">
|
||||
enum
|
||||
{
|
||||
${enum_name}
|
||||
% for idx,a in enumerate(fields):
|
||||
${'=' if idx == 0 else '+'} FieldTypes::${a.name}::${enum_name}
|
||||
% endfor
|
||||
};
|
||||
</%def>
|
||||
${expand_enum_per_field('MinBitLen')}
|
||||
${expand_enum_per_field('MaxBitLen')}
|
||||
|
||||
// Constants
|
||||
% for a in constants:
|
||||
% if a.cpp_use_enum:
|
||||
enum { ${a.name} = ${a.cpp_value} }; // ${a.init_expression}
|
||||
% else:
|
||||
static UAVCAN_CONSTEXPR typename ::uavcan::StorageType<ConstantTypes::${a.name}>::Type
|
||||
${a.name} = ${a.cpp_value}; // ${a.init_expression}
|
||||
%endif
|
||||
% endfor
|
||||
|
||||
// Fields
|
||||
% for a in fields:
|
||||
typename ::uavcan::StorageType<FieldTypes::${a.name}>::Type ${a.name};
|
||||
% endfor
|
||||
|
||||
${type_name}()
|
||||
% for idx,a in enumerate(fields):
|
||||
${':' if idx == 0 else ','} ${a.name}()
|
||||
% endfor
|
||||
{
|
||||
#if UAVCAN_DEBUG
|
||||
/*
|
||||
* Cross-checking MaxBitLen provided by the DSDL compiler.
|
||||
* This check shall never be performed in user code because MaxBitLen value
|
||||
* actually depends on the nested types, thus it is not invariant.
|
||||
*/
|
||||
::uavcan::StaticAssert<${max_bitlen} == MaxBitLen>::check();
|
||||
#endif
|
||||
}
|
||||
|
||||
<%def name="generate_codec_calls_per_field(call_name, self_parameter_type)">
|
||||
static int ${call_name}(${self_parameter_type} self, ::uavcan::ScalarCodec& codec,
|
||||
::uavcan::TailArrayOptimizationMode tao_mode = ::uavcan::TailArrayOptEnabled)
|
||||
{
|
||||
int res = 1;
|
||||
% for idx,a in enumerate(fields):
|
||||
res = FieldTypes::${a.name}::${call_name}(self.${a.name}, codec, \
|
||||
${'::uavcan::TailArrayOptDisabled' if (idx + 1) < len(fields) else 'tao_mode'});
|
||||
if (res <= 0)
|
||||
return res;
|
||||
% endfor
|
||||
return res;
|
||||
}
|
||||
</%def>
|
||||
${generate_codec_calls_per_field('encode', 'ParameterType')}
|
||||
${generate_codec_calls_per_field('decode', 'ReferenceType')}
|
||||
</%def>
|
||||
% if t.kind == t.KIND_SERVICE:
|
||||
struct Request
|
||||
{
|
||||
${generate_primary_body(t.short_name, t.get_max_bitlen_request(), t.request_fields, t.request_constants) | indent}
|
||||
};
|
||||
|
||||
struct Response
|
||||
{
|
||||
${generate_primary_body(t.short_name, t.get_max_bitlen_response(), t.response_fields, t.response_constants) | indent}
|
||||
};
|
||||
% else:
|
||||
${generate_primary_body(t.short_name, t.get_max_bitlen(), t.fields, t.constants)}
|
||||
% endif
|
||||
|
||||
/*
|
||||
* Static type info
|
||||
*/
|
||||
enum { DataTypeKind = ${t.cpp_kind} };
|
||||
% if t.has_default_dtid:
|
||||
enum { DefaultDataTypeID = ${t.default_dtid} };
|
||||
% else:
|
||||
// This type has no default data type ID
|
||||
% endif
|
||||
|
||||
static const char* getDataTypeFullName()
|
||||
{
|
||||
return "${t.full_name}";
|
||||
}
|
||||
|
||||
static void extendDataTypeSignature(::uavcan::DataTypeSignature& signature)
|
||||
{
|
||||
signature.extend(getDataTypeSignature());
|
||||
}
|
||||
|
||||
static ::uavcan::DataTypeSignature getDataTypeSignature()
|
||||
{
|
||||
::uavcan::DataTypeSignature signature(${hex(t.dsdl_signature)});
|
||||
<%def name="extend_signature_per_field(scope_prefix, fields)">
|
||||
% for a in fields:
|
||||
${scope_prefix}FieldTypes::${a.name}::extendDataTypeSignature(signature);
|
||||
% endfor
|
||||
</%def>
|
||||
% if t.kind == t.KIND_SERVICE:
|
||||
${extend_signature_per_field('Request::', t.request_fields)}
|
||||
${extend_signature_per_field('Response::', t.response_fields)}
|
||||
% else:
|
||||
${extend_signature_per_field('', t.fields)}
|
||||
% endif
|
||||
return signature;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO Stream operator
|
||||
|
||||
% if t.has_default_dtid:
|
||||
namespace
|
||||
{
|
||||
::uavcan::DefaultDataTypeRegistrator<${t.short_name}> _uavcan_gdtr_registrator_${t.short_name};
|
||||
}
|
||||
% endif
|
||||
|
||||
% for nsc in t.cpp_namespace_components:
|
||||
}
|
||||
% endfor
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
#
|
||||
|
||||
import sys, os
|
||||
import sys, os, argparse, logging
|
||||
from mako.template import Template
|
||||
|
||||
RUNNING_FROM_SRC_DIR = os.path.abspath(__file__).endswith(os.path.join('libuavcan', 'dsdl_compiler', 'dsdlc.py'))
|
||||
if RUNNING_FROM_SRC_DIR:
|
||||
@@ -15,4 +16,173 @@ if RUNNING_FROM_SRC_DIR:
|
||||
|
||||
from pyuavcan import dsdl
|
||||
|
||||
print('Hello')
|
||||
MAX_BITLEN_FOR_ENUM = 31
|
||||
CPP_HEADER_EXTENSION = 'hpp'
|
||||
TEMPLATE_FILENAME = os.path.join(os.path.dirname(__file__), 'data_type_template.hpp')
|
||||
|
||||
# -----------------
|
||||
|
||||
class DsdlCompilerException(Exception):
|
||||
pass
|
||||
|
||||
def pretty_filename(filename):
|
||||
a = os.path.abspath(filename)
|
||||
r = os.path.relpath(filename)
|
||||
return a if len(a) < len(r) else r
|
||||
|
||||
def type_output_filename(t):
|
||||
assert t.category == t.CATEGORY_COMPOUND
|
||||
return t.full_name.replace('.', os.path.sep) + '.' + CPP_HEADER_EXTENSION
|
||||
|
||||
def die(text):
|
||||
print(text, file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
def configure_logging(verbosity):
|
||||
fmt = '%(message)s'
|
||||
level = { 0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG }.get(verbosity or 0, logging.DEBUG)
|
||||
logging.basicConfig(stream=sys.stderr, level=level, format=fmt)
|
||||
|
||||
def run_parser(source_dir, search_dirs):
|
||||
try:
|
||||
types = dsdl.parse_namespace(source_dir, search_dirs)
|
||||
except dsdl.DsdlException as ex:
|
||||
errtext = str(ex) # TODO: gcc-style formatting
|
||||
die(errtext)
|
||||
logging.info('%d types from [%s] parsed successfully', len(types), source_dir)
|
||||
return types
|
||||
|
||||
def run_generator(types, dest_dir):
|
||||
try:
|
||||
dest_dir = os.path.abspath(dest_dir) # Removing '..'
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
for t in types:
|
||||
logging.info('Generating type %s', t.full_name)
|
||||
file_path = os.path.join(dest_dir, type_output_filename(t))
|
||||
dir_path = os.path.dirname(file_path)
|
||||
os.makedirs(dir_path, exist_ok=True)
|
||||
text = generate_one_type(t)
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(text)
|
||||
except Exception as ex:
|
||||
logging.info('Generator error', exc_info=True)
|
||||
die(str(ex))
|
||||
|
||||
def type_to_cpp_type(t):
|
||||
if t.category == t.CATEGORY_PRIMITIVE:
|
||||
cast_mode = {
|
||||
t.CAST_MODE_SATURATED: '::uavcan::CastModeSaturate',
|
||||
t.CAST_MODE_TRUNCATED: '::uavcan::CastModeTruncate',
|
||||
}[t.cast_mode]
|
||||
if t.kind == t.KIND_FLOAT:
|
||||
return '::uavcan::FloatSpec<%d, %s>' % (t.bitlen, cast_mode)
|
||||
else:
|
||||
signedness = {
|
||||
t.KIND_BOOLEAN: '::uavcan::SignednessUnsigned',
|
||||
t.KIND_UNSIGNED_INT: '::uavcan::SignednessUnsigned',
|
||||
t.KIND_SIGNED_INT: '::uavcan::SignednessSigned',
|
||||
}[t.kind]
|
||||
return '::uavcan::IntegerSpec<%d, %s, %s>' % (t.bitlen, signedness, cast_mode)
|
||||
elif t.category == t.CATEGORY_ARRAY:
|
||||
value_type = type_to_cpp_type(t.value_type)
|
||||
mode = {
|
||||
t.MODE_STATIC: '::uavcan::ArrayModeStatic',
|
||||
t.MODE_DYNAMIC: '::uavcan::ArrayModeDynamic',
|
||||
}[t.mode]
|
||||
return '::uavcan::Array<%s, %s, %d>' % (value_type, mode, t.max_size)
|
||||
elif t.category == t.CATEGORY_COMPOUND:
|
||||
return '::' + t.full_name.replace('.', '::')
|
||||
else:
|
||||
raise DsdlCompilerException('Unknown type category: %s' % t.category)
|
||||
|
||||
def generate_one_type(t):
|
||||
t.short_name = t.full_name.split('.')[-1]
|
||||
|
||||
# Dependencies (no duplicates)
|
||||
def fields_includes(fields):
|
||||
return set(type_output_filename(x.type) for x in fields if x.type.category == x.type.CATEGORY_COMPOUND)
|
||||
|
||||
if t.kind == t.KIND_MESSAGE:
|
||||
t.cpp_includes = fields_includes(t.fields)
|
||||
else:
|
||||
t.cpp_includes = fields_includes(t.request_fields + t.response_fields)
|
||||
|
||||
t.cpp_namespace_components = t.full_name.split('.')[:-1]
|
||||
t.has_default_dtid = t.default_dtid is not None
|
||||
|
||||
# Attribute types
|
||||
def inject_cpp_types(attributes):
|
||||
for a in attributes:
|
||||
a.cpp_type = type_to_cpp_type(a.type)
|
||||
|
||||
if t.kind == t.KIND_MESSAGE:
|
||||
inject_cpp_types(t.fields)
|
||||
inject_cpp_types(t.constants)
|
||||
else:
|
||||
inject_cpp_types(t.request_fields)
|
||||
inject_cpp_types(t.request_constants)
|
||||
inject_cpp_types(t.response_fields)
|
||||
inject_cpp_types(t.response_constants)
|
||||
|
||||
# Constant properties
|
||||
def inject_constant_info(constants):
|
||||
for c in constants:
|
||||
if c.type.kind == c.type.KIND_FLOAT:
|
||||
c.cpp_use_enum = False
|
||||
numeric_limits = '::std::numeric_limits<typename ::uavcan::StorageType<typename ConstantTypes::%s>::Type>' % c.name
|
||||
numeric_limits_inf = numeric_limits + '::infinity()'
|
||||
special_values = {
|
||||
'inf': numeric_limits_inf,
|
||||
'+inf': numeric_limits_inf,
|
||||
'-inf': '-' + numeric_limits_inf,
|
||||
'nan': numeric_limits + '::quiet_NaN()',
|
||||
}
|
||||
if c.string_value in special_values:
|
||||
c.cpp_value = special_values[c.string_value]
|
||||
else:
|
||||
float(c.string_value) # making sure that this is a valid float literal
|
||||
c.cpp_value = c.string_value
|
||||
else:
|
||||
c.cpp_use_enum = c.value >= 0 and c.type.bitlen <= MAX_BITLEN_FOR_ENUM
|
||||
c.cpp_value = c.string_value
|
||||
if t.kind == t.KIND_MESSAGE:
|
||||
inject_constant_info(t.constants)
|
||||
else:
|
||||
inject_constant_info(t.request_constants)
|
||||
inject_constant_info(t.response_constants)
|
||||
|
||||
# Data type kind
|
||||
t.cpp_kind = {
|
||||
t.KIND_MESSAGE: '::uavcan::DataTypeKindMessage',
|
||||
t.KIND_SERVICE: '::uavcan::DataTypeKindService',
|
||||
}[t.kind]
|
||||
|
||||
# Generation
|
||||
template = Template(filename=TEMPLATE_FILENAME)
|
||||
text = template.render(t=t)
|
||||
text = '\n'.join(x.rstrip() for x in text.splitlines())
|
||||
text = text.replace('\n\n\n\n', '\n\n').replace('\n\n\n', '\n\n')
|
||||
text = text.replace('{\n\n ', '{\n ')
|
||||
return text
|
||||
|
||||
# -----------------
|
||||
|
||||
DESCRIPTION = '''UAVCAN DSDL compiler. Takes an input directory that contains an hierarchy of DSDL
|
||||
definitions and converts it into compatible hierarchy of C++ types for libuavcan.'''
|
||||
|
||||
DEFAULT_OUTDIR = './dsdlc_output'
|
||||
|
||||
argparser = argparse.ArgumentParser(description=DESCRIPTION)
|
||||
argparser.add_argument('source_dir', help='source directory with DSDL definitions')
|
||||
argparser.add_argument('--verbose', '-v', action='count', help='verbosity level (-v, -vv)')
|
||||
argparser.add_argument('--outdir', '-O', default=DEFAULT_OUTDIR, help='output directory, default %s' % DEFAULT_OUTDIR)
|
||||
argparser.add_argument('--incdir', '-I', default=[], action='append', help='nested type namespaces')
|
||||
args = argparser.parse_args()
|
||||
|
||||
configure_logging(args.verbose)
|
||||
|
||||
types = run_parser(args.source_dir, args.incdir)
|
||||
if not types:
|
||||
die('No type definitions were found')
|
||||
|
||||
run_generator(types, args.outdir)
|
||||
|
||||
@@ -2,5 +2,10 @@
|
||||
# Test file
|
||||
#
|
||||
|
||||
int2 SMALL_CONST = -2
|
||||
int61 CONST = -123456789
|
||||
float16 FLOAT_CONST = 1.23
|
||||
float16 FLOAT_CONST2 = nan
|
||||
float16 FLOAT_CONST3 = -inf
|
||||
root_b.TypeInRootB type_in_root_b
|
||||
root_a.ns2.TypeInNs2[<5] type_in_ns_2
|
||||
|
||||
@@ -92,13 +92,14 @@ class CompoundType(Type):
|
||||
KIND_SERVICE = 0
|
||||
KIND_MESSAGE = 1
|
||||
|
||||
def __init__(self, full_name, kind, dsdl_signature, dsdl_path, default_dtid, filename):
|
||||
def __init__(self, full_name, kind, dsdl_signature, dsdl_path, default_dtid, filename, source_text):
|
||||
super().__init__(full_name, Type.CATEGORY_COMPOUND)
|
||||
self.dsdl_signature = dsdl_signature
|
||||
self.dsdl_path = dsdl_path
|
||||
self.default_dtid = default_dtid
|
||||
self.kind = kind
|
||||
self.filename = filename
|
||||
self.source_text = source_text
|
||||
max_bitlen_sum = lambda fields: sum([x.type.get_max_bitlen() for x in fields])
|
||||
if kind == CompoundType.KIND_SERVICE:
|
||||
self.request_fields = []
|
||||
@@ -362,10 +363,10 @@ class Parser:
|
||||
try:
|
||||
filename = os.path.abspath(filename)
|
||||
with open(filename) as f:
|
||||
text = f.read()
|
||||
source_text = f.read()
|
||||
|
||||
full_typename, default_dtid = self._full_typename_and_dtid_from_filename(filename)
|
||||
numbered_lines = list(self._tokenize(text))
|
||||
numbered_lines = list(self._tokenize(source_text))
|
||||
all_attributes_names = set()
|
||||
fields, constants, resp_fields, resp_constants = [], [], [], []
|
||||
response_part = False
|
||||
@@ -396,7 +397,7 @@ class Parser:
|
||||
dsdl_signature = self._compute_dsdl_signature(full_typename, fields, constants,
|
||||
resp_fields, resp_constants)
|
||||
typedef = CompoundType(full_typename, CompoundType.KIND_SERVICE, dsdl_signature, filename,
|
||||
default_dtid, filename)
|
||||
default_dtid, filename, source_text)
|
||||
typedef.request_fields = fields
|
||||
typedef.request_constants = constants
|
||||
typedef.response_fields = resp_fields
|
||||
@@ -406,7 +407,7 @@ class Parser:
|
||||
else:
|
||||
dsdl_signature = self._compute_dsdl_signature(full_typename, fields, constants)
|
||||
typedef = CompoundType(full_typename, CompoundType.KIND_MESSAGE, dsdl_signature, filename,
|
||||
default_dtid, filename)
|
||||
default_dtid, filename, source_text)
|
||||
typedef.fields = fields
|
||||
typedef.constants = constants
|
||||
max_bitlen = typedef.get_max_bitlen()
|
||||
|
||||
Reference in New Issue
Block a user