3 mavlink python parse functions 5 Copyright Andrew Tridgell 2011 6 Released under GNU GPL version 3 or later 8 from __future__
import print_function
9 from builtins
import range
10 from builtins
import object
17 import xml.parsers.expat
24 FLAG_HAVE_TARGET_SYSTEM = 1
25 FLAG_HAVE_TARGET_COMPONENT = 2
28 def __init__(self, message, inner_exception=None):
36 def __init__(self, name, type, print_format, xml, description='', enum='', display='', units=''):
53 'uint8_t_mavlink_version' : 1,
62 if type==
'uint8_t_mavlink_version':
69 assert type[-1:] ==
']' 77 elif (type+
"_t")
in lengths:
89 '''generate a testsuite value for a MAVField''' 92 elif self.
type ==
'float':
93 return 17.0 + self.wire_offset*7 + i
94 elif self.
type ==
'double':
95 return 123.0 + self.wire_offset*7 + i
96 elif self.
type ==
'char':
97 return chr(ord(
'A') + (self.wire_offset + i)%26)
98 elif self.
type in [
'int8_t',
'uint8_t' ]:
99 return (5 + self.wire_offset*67 + i) & 0xFF
100 elif self.
type in [
'int16_t',
'uint16_t']:
101 return (17235 + self.wire_offset*52 + i) & 0xFFFF
102 elif self.
type in [
'int32_t',
'uint32_t']:
103 return (963497464 + self.wire_offset*52 + i)&0xFFFFFFFF
104 elif self.
type in [
'int64_t',
'uint64_t']:
105 return 93372036854775807 + self.wire_offset*63 + i
110 '''set a testsuite value for a MAVField''' 125 def __init__(self, name, id, linenumber, description=''):
136 '''return number of non-extended fields''' 142 def __init__(self, index, description='', label='', units='', enum='', increment='', minValue='', maxValue='', reserved=False, default=''):
158 if not description.strip()
and self.
reserved:
164 def __init__(self, name, value, description='', end_marker=False, autovalue=False, origin_file='', origin_line=0):
175 def __init__(self, name, linenumber, description=''):
184 '''parse a mavlink XML file''' 185 def __init__(self, filename, wire_protocol_version=PROTOCOL_0_9):
188 if self.basename.lower().endswith(
".xml"):
201 if wire_protocol_version == PROTOCOL_0_9:
209 elif wire_protocol_version == PROTOCOL_1_0:
217 elif wire_protocol_version == PROTOCOL_2_0:
226 print(
"Unknown wire protocol version")
227 print(
"Available versions are: %s %s" % (PROTOCOL_0_9, PROTOCOL_1_0, PROTOCOL_2_0))
228 raise MAVParseError(
'Unknown MAVLink wire protocol version %s' % wire_protocol_version)
232 def check_attrs(attrs, check, where):
235 raise MAVParseError(
'expected missing %s "%s" attribute at %s:%u' % (
236 where, c, filename, p.CurrentLineNumber))
238 def start_element(name, attrs):
239 in_element_list.append(name)
240 in_element =
'.'.join(in_element_list)
242 if in_element ==
"mavlink.messages.message":
243 check_attrs(attrs, [
'name',
'id'],
'message')
244 self.message.append(
MAVType(attrs[
'name'], attrs[
'id'], p.CurrentLineNumber))
245 elif in_element ==
"mavlink.messages.message.extensions":
247 elif in_element ==
"mavlink.messages.message.field":
248 check_attrs(attrs, [
'name',
'type'],
'field')
249 print_format = attrs.get(
'print_format',
None)
250 enum = attrs.get(
'enum',
'')
251 display = attrs.get(
'display',
'')
252 units = attrs.get(
'units',
'')
254 units =
'[' + units +
']' 255 new_field =
MAVField(attrs[
'name'], attrs[
'type'], print_format, self, enum=enum, display=display, units=units)
257 self.
message[-1].fields.append(new_field)
258 elif in_element ==
"mavlink.enums.enum":
259 check_attrs(attrs, [
'name'],
'enum')
260 self.enum.append(
MAVEnum(attrs[
'name'], p.CurrentLineNumber))
261 elif in_element ==
"mavlink.enums.enum.entry":
262 check_attrs(attrs, [
'name'],
'enum entry')
265 value = eval(attrs[
'value'])
268 value = self.
enum[-1].highest_value + 1
271 if (self.
enum[-1].start_value
is None or value < self.
enum[-1].start_value):
272 self.
enum[-1].start_value = value
274 if (value > self.
enum[-1].highest_value):
275 self.
enum[-1].highest_value = value
277 self.
enum[-1].entry.append(
MAVEnumEntry(attrs[
'name'], value,
'',
False, autovalue, self.
filename, p.CurrentLineNumber))
278 elif in_element ==
"mavlink.enums.enum.entry.param":
279 check_attrs(attrs, [
'index'],
'enum param')
280 self.
enum[-1].entry[-1].param.append(
282 label=attrs.get(
'label',
''), units=attrs.get(
'units',
''),
283 enum=attrs.get(
'enum',
''), increment=attrs.get(
'increment',
''),
284 minValue=attrs.get(
'minValue',
''),
285 maxValue=attrs.get(
'maxValue',
''), default=attrs.get(
'default',
'0'),
286 reserved=attrs.get(
'reserved',
False) ))
288 def is_target_system_field(m, f):
289 if f.name ==
'target_system':
291 if m.name ==
"MANUAL_CONTROL" and f.name ==
"target":
295 def end_element(name):
296 in_element_list.pop()
299 in_element =
'.'.join(in_element_list)
300 if in_element ==
"mavlink.messages.message.description":
301 self.
message[-1].description += data
302 elif in_element ==
"mavlink.messages.message.field":
304 self.
message[-1].fields[-1].description += data
305 elif in_element ==
"mavlink.enums.enum.description":
306 self.
enum[-1].description += data
307 elif in_element ==
"mavlink.enums.enum.entry.description":
308 self.
enum[-1].entry[-1].description += data
309 elif in_element ==
"mavlink.enums.enum.entry.param":
310 self.
enum[-1].entry[-1].param[-1].set_description(data.strip())
311 elif in_element ==
"mavlink.version":
313 elif in_element ==
"mavlink.include":
314 self.include.append(data)
316 f = open(filename, mode=
'rb')
317 p = xml.parsers.expat.ParserCreate()
318 p.StartElementHandler = start_element
319 p.EndElementHandler = end_element
320 p.CharacterDataHandler = char_data
326 for current_enum
in self.
enum:
327 if not 'MAV_CMD' in current_enum.name:
329 print(current_enum.name)
330 for enum_entry
in current_enum.entry:
331 print(enum_entry.name)
332 if len(enum_entry.param) == 7:
335 for param_index
in range (1,8):
336 params_dict[param_index] =
MAVEnumParam(param_index, label=
'', units=
'', enum=
'', increment=
'',
337 minValue=
'', maxValue=
'', default=
'0', reserved=
'True')
339 for a_param
in enum_entry.param:
340 params_dict[
int(a_param.index)] = a_param
341 enum_entry.param=params_dict.values()
361 print(
"Ignoring MAVLink2 message %s" % m.name)
369 m.wire_min_length = 0
372 m.ordered_fieldnames = []
373 m.ordered_fieldtypes = []
376 m.target_system_ofs = 0
377 m.target_component_ofs = 0
381 sort_end = m.base_fields()
382 m.ordered_fields = sorted(m.fields[:sort_end],
383 key=operator.attrgetter(
'type_length'),
385 m.ordered_fields.extend(m.fields[sort_end:])
387 m.ordered_fields = m.fields
389 m.fieldnames.append(f.name)
392 m.fieldlengths.append(1)
393 elif L > 1
and f.type ==
'char':
394 m.fieldlengths.append(1)
396 m.fieldlengths.append(L)
397 m.fieldtypes.append(f.type)
398 for i
in range(len(m.ordered_fields)):
399 f = m.ordered_fields[i]
400 f.wire_offset = m.wire_length
401 m.wire_length += f.wire_length
402 if m.extensions_start
is None or i < m.extensions_start:
403 m.wire_min_length = m.wire_length
404 m.ordered_fieldnames.append(f.name)
405 m.ordered_fieldtypes.append(f.type)
407 if f.name.find(
'[') != -1:
408 raise MAVParseError(
"invalid field name with array descriptor %s" % f.name)
410 if is_target_system_field(m, f):
411 m.message_flags |= FLAG_HAVE_TARGET_SYSTEM
412 m.target_system_ofs = f.wire_offset
413 elif f.name ==
'target_component':
414 m.message_flags |= FLAG_HAVE_TARGET_COMPONENT
415 m.target_component_ofs = f.wire_offset
416 m.num_fields = len(m.fieldnames)
417 if m.num_fields > 64:
418 raise MAVParseError(
"num_fields=%u : Maximum number of field names allowed is" % (
434 if m.wire_length+8 > 64:
435 print(
"Note: message %s is longer than 64 bytes long (%u bytes), which can cause fragmentation since many radio modems use 64 bytes as maximum air transfer unit." % (m.name, m.wire_length+8))
438 return "MAVXML for %s from %s (%u message, %u enums)" % (
443 '''calculate a 8-bit checksum of the key fields of a message, so we 444 can detect incompatible XML changes''' 445 from .mavcrc
import x25crc
447 crc.accumulate_str(msg.name +
' ')
450 crc_end = msg.base_fields()
451 for i
in range(crc_end):
452 f = msg.ordered_fields[i]
453 crc.accumulate_str(f.type +
' ')
454 crc.accumulate_str(f.name +
' ')
456 crc.accumulate([f.array_length])
457 return (crc.crc&0xFF) ^ (crc.crc>>8)
460 '''merge enums between XML files''' 465 if enum.name
in emap:
466 emapitem = emap[enum.name]
468 if (emapitem.start_value <= enum.highest_value
and emapitem.highest_value >= enum.start_value):
469 for entry
in emapitem.entry:
471 if entry.value <= enum.highest_value
and entry.autovalue
is True:
472 entry.value = enum.highest_value + 1
473 enum.highest_value = entry.value
475 emapitem.entry.extend(enum.entry)
476 if not emapitem.description:
477 emapitem.description = enum.description
478 print(
"Merged enum %s" % enum.name)
480 newenums.append(enum)
481 emap[enum.name] = enum
485 emap[e].entry = sorted(emap[e].entry,
486 key=operator.attrgetter(
'value'),
489 emap[e].entry.append(
MAVEnumEntry(
"%s_ENUM_END" % emap[e].name,
490 emap[e].entry[-1].value+1, end_marker=
True))
493 '''check for duplicate message IDs''' 504 print(
"ERROR: Duplicate message id %u for %s (%s:%u) also used by %s" % (
507 x.filename, m.linenumber,
512 if f.name
in fieldset:
513 print(
"ERROR: Duplicate field %s in message %s (%s:%u)" % (
515 x.filename, m.linenumber))
518 msgmap[key] =
'%s (%s:%u)' % (m.name, x.filename, m.linenumber)
520 if m.name
in msg_name_map:
521 print(
"ERROR: Duplicate message name %s for id:%u (%s:%u) also used by %s" % (
524 x.filename, m.linenumber,
525 msg_name_map[m.name]))
527 msg_name_map[m.name] =
'%s (%s:%u)' % (m.id, x.filename, m.linenumber)
529 for entry
in enum.entry:
530 if entry.autovalue
is True and "common.xml" not in entry.origin_file:
531 print(
"Note: An enum value was auto-generated: %s = %u" % (entry.name, entry.value))
532 s1 =
"%s.%s" % (enum.name, entry.name)
533 s2 =
"%s.%s" % (enum.name, entry.value)
534 if s1
in enummap
or s2
in enummap:
535 print(
"ERROR: Duplicate enum %s:\n\t%s = %s @ %s:%u\n\t%s" % (
536 "names" if s1
in enummap
else "values",
537 s1, entry.value, entry.origin_file, entry.origin_line,
538 enummap.get(s1)
or enummap.get(s2)))
540 enummap[s1] = enummap[s2] =
"%s.%s = %s @ %s:%u" % (enum.name, entry.name, entry.value, entry.origin_file, entry.origin_line)
547 '''count total number of msgs''' 550 count += len(x.message)
556 except OSError
as exc:
557 if exc.errno != errno.EEXIST:
message_target_component_ofs
def gen_test_value(self, i)
def __init__(self, name, id, linenumber, description='')
def __init__(self, name, linenumber, description='')
message_target_system_ofs
def check_duplicates(xml)
def __init__(self, name, type, print_format, xml, description='', enum='', display='', units='')
def __init__(self, message, inner_exception=None)
def __init__(self, index, description='', label='', units='', enum='', increment='', minValue='', maxValue='', reserved=False, default='')
def __init__(self, filename, wire_protocol_version=PROTOCOL_0_9)
def message_checksum(msg)
def set_description(self, description)
def __init__(self, name, value, description='', end_marker=False, autovalue=False, origin_file='', origin_line=0)