mavgen_python.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931
  1. #!/usr/bin/env python
  2. '''
  3. parse a MAVLink protocol XML file and generate a python implementation
  4. Copyright Andrew Tridgell 2011
  5. Released under GNU GPL version 3 or later
  6. '''
  7. from __future__ import print_function
  8. from builtins import range
  9. import os
  10. import textwrap
  11. from . import mavtemplate
  12. t = mavtemplate.MAVTemplate()
  13. def generate_preamble(outf, msgs, basename, args, xml):
  14. print("Generating preamble")
  15. t.write(outf, """
  16. '''
  17. MAVLink protocol implementation (auto-generated by mavgen.py)
  18. Generated from: ${FILELIST}
  19. Note: this file has been auto-generated. DO NOT EDIT
  20. '''
  21. from __future__ import print_function
  22. from builtins import range
  23. from builtins import object
  24. import struct, array, time, json, os, sys, platform
  25. from ...generator.mavcrc import x25crc
  26. import hashlib
  27. WIRE_PROTOCOL_VERSION = '${WIRE_PROTOCOL_VERSION}'
  28. DIALECT = '${DIALECT}'
  29. PROTOCOL_MARKER_V1 = 0xFE
  30. PROTOCOL_MARKER_V2 = 0xFD
  31. HEADER_LEN_V1 = 6
  32. HEADER_LEN_V2 = 10
  33. MAVLINK_SIGNATURE_BLOCK_LEN = 13
  34. MAVLINK_IFLAG_SIGNED = 0x01
  35. native_supported = platform.system() != 'Windows' # Not yet supported on other dialects
  36. native_force = 'MAVNATIVE_FORCE' in os.environ # Will force use of native code regardless of what client app wants
  37. native_testing = 'MAVNATIVE_TESTING' in os.environ # Will force both native and legacy code to be used and their results compared
  38. if native_supported and float(WIRE_PROTOCOL_VERSION) <= 1:
  39. try:
  40. import mavnative
  41. except ImportError:
  42. print('ERROR LOADING MAVNATIVE - falling back to python implementation')
  43. native_supported = False
  44. else:
  45. # mavnative isn't supported for MAVLink2 yet
  46. native_supported = False
  47. # some base types from mavlink_types.h
  48. MAVLINK_TYPE_CHAR = 0
  49. MAVLINK_TYPE_UINT8_T = 1
  50. MAVLINK_TYPE_INT8_T = 2
  51. MAVLINK_TYPE_UINT16_T = 3
  52. MAVLINK_TYPE_INT16_T = 4
  53. MAVLINK_TYPE_UINT32_T = 5
  54. MAVLINK_TYPE_INT32_T = 6
  55. MAVLINK_TYPE_UINT64_T = 7
  56. MAVLINK_TYPE_INT64_T = 8
  57. MAVLINK_TYPE_FLOAT = 9
  58. MAVLINK_TYPE_DOUBLE = 10
  59. class MAVLink_header(object):
  60. '''MAVLink message header'''
  61. def __init__(self, msgId, incompat_flags=0, compat_flags=0, mlen=0, seq=0, srcSystem=0, srcComponent=0):
  62. self.mlen = mlen
  63. self.seq = seq
  64. self.srcSystem = srcSystem
  65. self.srcComponent = srcComponent
  66. self.msgId = msgId
  67. self.incompat_flags = incompat_flags
  68. self.compat_flags = compat_flags
  69. def pack(self, force_mavlink1=False):
  70. if WIRE_PROTOCOL_VERSION == '2.0' and not force_mavlink1:
  71. return struct.pack('<BBBBBBBHB', ${PROTOCOL_MARKER}, self.mlen,
  72. self.incompat_flags, self.compat_flags,
  73. self.seq, self.srcSystem, self.srcComponent,
  74. self.msgId&0xFFFF, self.msgId>>16)
  75. return struct.pack('<BBBBBB', PROTOCOL_MARKER_V1, self.mlen, self.seq,
  76. self.srcSystem, self.srcComponent, self.msgId)
  77. class MAVLink_message(object):
  78. '''base MAVLink message class'''
  79. def __init__(self, msgId, name):
  80. self._header = MAVLink_header(msgId)
  81. self._payload = None
  82. self._msgbuf = None
  83. self._crc = None
  84. self._fieldnames = []
  85. self._type = name
  86. self._signed = False
  87. self._link_id = None
  88. def format_attr(self, field):
  89. '''override field getter'''
  90. raw_attr = getattr(self,field)
  91. if isinstance(raw_attr, bytes):
  92. raw_attr = raw_attr.decode("utf-8").rstrip("\\00")
  93. return raw_attr
  94. def get_msgbuf(self):
  95. if isinstance(self._msgbuf, bytearray):
  96. return self._msgbuf
  97. return bytearray(self._msgbuf)
  98. def get_header(self):
  99. return self._header
  100. def get_payload(self):
  101. return self._payload
  102. def get_crc(self):
  103. return self._crc
  104. def get_fieldnames(self):
  105. return self._fieldnames
  106. def get_type(self):
  107. return self._type
  108. def get_msgId(self):
  109. return self._header.msgId
  110. def get_srcSystem(self):
  111. return self._header.srcSystem
  112. def get_srcComponent(self):
  113. return self._header.srcComponent
  114. def get_seq(self):
  115. return self._header.seq
  116. def get_signed(self):
  117. return self._signed
  118. def get_link_id(self):
  119. return self._link_id
  120. def __str__(self):
  121. ret = '%s {' % self._type
  122. for a in self._fieldnames:
  123. v = self.format_attr(a)
  124. ret += '%s : %s, ' % (a, v)
  125. ret = ret[0:-2] + '}'
  126. return ret
  127. def __ne__(self, other):
  128. return not self.__eq__(other)
  129. def __eq__(self, other):
  130. if other is None:
  131. return False
  132. if self.get_type() != other.get_type():
  133. return False
  134. # We do not compare CRC because native code doesn't provide it
  135. #if self.get_crc() != other.get_crc():
  136. # return False
  137. if self.get_seq() != other.get_seq():
  138. return False
  139. if self.get_srcSystem() != other.get_srcSystem():
  140. return False
  141. if self.get_srcComponent() != other.get_srcComponent():
  142. return False
  143. for a in self._fieldnames:
  144. if self.format_attr(a) != other.format_attr(a):
  145. return False
  146. return True
  147. def to_dict(self):
  148. d = dict({})
  149. d['mavpackettype'] = self._type
  150. for a in self._fieldnames:
  151. d[a] = self.format_attr(a)
  152. return d
  153. def to_json(self):
  154. return json.dumps(self.to_dict())
  155. def sign_packet(self, mav):
  156. h = hashlib.new('sha256')
  157. self._msgbuf += struct.pack('<BQ', mav.signing.link_id, mav.signing.timestamp)[:7]
  158. h.update(mav.signing.secret_key)
  159. h.update(self._msgbuf)
  160. sig = h.digest()[:6]
  161. self._msgbuf += sig
  162. mav.signing.timestamp += 1
  163. def pack(self, mav, crc_extra, payload, force_mavlink1=False):
  164. plen = len(payload)
  165. if WIRE_PROTOCOL_VERSION != '1.0' and not force_mavlink1:
  166. # in MAVLink2 we can strip trailing zeros off payloads. This allows for simple
  167. # variable length arrays and smaller packets
  168. while plen > 1 and payload[plen-1] == chr(0):
  169. plen -= 1
  170. self._payload = payload[:plen]
  171. incompat_flags = 0
  172. if mav.signing.sign_outgoing:
  173. incompat_flags |= MAVLINK_IFLAG_SIGNED
  174. self._header = MAVLink_header(self._header.msgId,
  175. incompat_flags=incompat_flags, compat_flags=0,
  176. mlen=len(self._payload), seq=mav.seq,
  177. srcSystem=mav.srcSystem, srcComponent=mav.srcComponent)
  178. self._msgbuf = self._header.pack(force_mavlink1=force_mavlink1) + self._payload
  179. crc = x25crc(self._msgbuf[1:])
  180. if ${crc_extra}: # using CRC extra
  181. crc.accumulate_str(struct.pack('B', crc_extra))
  182. self._crc = crc.crc
  183. self._msgbuf += struct.pack('<H', self._crc)
  184. if mav.signing.sign_outgoing and not force_mavlink1:
  185. self.sign_packet(mav)
  186. return self._msgbuf
  187. """, {'FILELIST': ",".join(args),
  188. 'PROTOCOL_MARKER': xml.protocol_marker,
  189. 'DIALECT': os.path.splitext(os.path.basename(basename))[0],
  190. 'crc_extra': xml.crc_extra,
  191. 'WIRE_PROTOCOL_VERSION': xml.wire_protocol_version})
  192. def generate_enums(outf, enums):
  193. print("Generating enums")
  194. outf.write('''
  195. # enums
  196. class EnumEntry(object):
  197. def __init__(self, name, description):
  198. self.name = name
  199. self.description = description
  200. self.param = {}
  201. enums = {}
  202. ''')
  203. wrapper = textwrap.TextWrapper(initial_indent="", subsequent_indent=" # ")
  204. for e in enums:
  205. outf.write("\n# %s\n" % e.name)
  206. outf.write("enums['%s'] = {}\n" % e.name)
  207. for entry in e.entry:
  208. outf.write("%s = %u # %s\n" % (entry.name, entry.value, wrapper.fill(entry.description)))
  209. outf.write("enums['%s'][%d] = EnumEntry('%s', '''%s''')\n" % (e.name,
  210. int(entry.value), entry.name,
  211. entry.description))
  212. for param in entry.param:
  213. outf.write("enums['%s'][%d].param[%d] = '''%s'''\n" % (e.name,
  214. int(entry.value),
  215. int(param.index),
  216. param.description))
  217. def generate_message_ids(outf, msgs):
  218. print("Generating message IDs")
  219. outf.write("\n# message IDs\n")
  220. outf.write("MAVLINK_MSG_ID_BAD_DATA = -1\n")
  221. for m in msgs:
  222. outf.write("MAVLINK_MSG_ID_%s = %u\n" % (m.name.upper(), m.id))
  223. def byname_hash_from_field_attribute(m, attribute):
  224. strings = []
  225. for field in m.fields:
  226. value = getattr(field, attribute, None)
  227. if value is None or value == "":
  228. continue
  229. if attribute == 'units':
  230. # hack; remove the square brackets further up
  231. if value[0] == "[":
  232. value = value[1:-1]
  233. strings.append('"%s": "%s"' % (field.name, value))
  234. return ", ".join(strings)
  235. def generate_classes(outf, msgs):
  236. print("Generating class definitions")
  237. wrapper = textwrap.TextWrapper(initial_indent=" ", subsequent_indent=" ")
  238. for m in msgs:
  239. classname = "MAVLink_%s_message" % m.name.lower()
  240. fieldname_str = ", ".join(["'%s'" % s for s in m.fieldnames])
  241. ordered_fieldname_str = ", ".join(["'%s'" % s for s in m.ordered_fieldnames])
  242. fielddisplays_str = byname_hash_from_field_attribute(m, "display")
  243. fieldenums_str = byname_hash_from_field_attribute(m, "enum")
  244. fieldunits_str = byname_hash_from_field_attribute(m, "units")
  245. fieldtypes_str = ", ".join(["'%s'" % s for s in m.fieldtypes])
  246. outf.write("""
  247. class %s(MAVLink_message):
  248. '''
  249. %s
  250. '''
  251. id = MAVLINK_MSG_ID_%s
  252. name = '%s'
  253. fieldnames = [%s]
  254. ordered_fieldnames = [%s]
  255. fieldtypes = [%s]
  256. fielddisplays_by_name = {%s}
  257. fieldenums_by_name = {%s}
  258. fieldunits_by_name = {%s}
  259. format = '%s'
  260. native_format = bytearray('%s', 'ascii')
  261. orders = %s
  262. lengths = %s
  263. array_lengths = %s
  264. crc_extra = %s
  265. unpacker = struct.Struct('%s')
  266. def __init__(self""" % (classname, wrapper.fill(m.description.strip()),
  267. m.name.upper(),
  268. m.name.upper(),
  269. fieldname_str,
  270. ordered_fieldname_str,
  271. fieldtypes_str,
  272. fielddisplays_str,
  273. fieldenums_str,
  274. fieldunits_str,
  275. m.fmtstr,
  276. m.native_fmtstr,
  277. m.order_map,
  278. m.len_map,
  279. m.array_len_map,
  280. m.crc_extra,
  281. m.fmtstr))
  282. for i in range(len(m.fields)):
  283. fname = m.fieldnames[i]
  284. if m.extensions_start is not None and i >= m.extensions_start:
  285. fdefault = m.fielddefaults[i]
  286. outf.write(", %s=%s" % (fname, fdefault))
  287. else:
  288. outf.write(", %s" % fname)
  289. outf.write("):\n")
  290. outf.write(" MAVLink_message.__init__(self, %s.id, %s.name)\n" % (classname, classname))
  291. outf.write(" self._fieldnames = %s.fieldnames\n" % (classname))
  292. for f in m.fields:
  293. outf.write(" self.%s = %s\n" % (f.name, f.name))
  294. outf.write("""
  295. def pack(self, mav, force_mavlink1=False):
  296. return MAVLink_message.pack(self, mav, %u, struct.pack('%s'""" % (m.crc_extra, m.fmtstr))
  297. for field in m.ordered_fields:
  298. if (field.type != "char" and field.array_length > 1):
  299. for i in range(field.array_length):
  300. outf.write(", self.{0:s}[{1:d}]".format(field.name, i))
  301. else:
  302. outf.write(", self.{0:s}".format(field.name))
  303. outf.write("), force_mavlink1=force_mavlink1)\n")
  304. def native_mavfmt(field):
  305. '''work out the struct format for a type (in a form expected by mavnative)'''
  306. map = {
  307. 'float': 'f',
  308. 'double': 'd',
  309. 'char': 'c',
  310. 'int8_t': 'b',
  311. 'uint8_t': 'B',
  312. 'uint8_t_mavlink_version': 'v',
  313. 'int16_t': 'h',
  314. 'uint16_t': 'H',
  315. 'int32_t': 'i',
  316. 'uint32_t': 'I',
  317. 'int64_t': 'q',
  318. 'uint64_t': 'Q',
  319. }
  320. return map[field.type]
  321. def mavfmt(field):
  322. '''work out the struct format for a type'''
  323. map = {
  324. 'float': 'f',
  325. 'double': 'd',
  326. 'char': 'c',
  327. 'int8_t': 'b',
  328. 'uint8_t': 'B',
  329. 'uint8_t_mavlink_version': 'B',
  330. 'int16_t': 'h',
  331. 'uint16_t': 'H',
  332. 'int32_t': 'i',
  333. 'uint32_t': 'I',
  334. 'int64_t': 'q',
  335. 'uint64_t': 'Q',
  336. }
  337. if field.array_length:
  338. if field.type == 'char':
  339. return str(field.array_length)+'s'
  340. return str(field.array_length)+map[field.type]
  341. return map[field.type]
  342. def mavdefault(field):
  343. '''returns default value for field (as string) for mavlink2 extensions'''
  344. if field.type == 'char':
  345. default_value = "''"
  346. else:
  347. default_value = "0"
  348. if field.array_length == 0:
  349. return default_value
  350. return "[" + ",".join([default_value] * field.array_length) + "]"
  351. def generate_mavlink_class(outf, msgs, xml):
  352. print("Generating MAVLink class")
  353. outf.write("\n\nmavlink_map = {\n")
  354. for m in msgs:
  355. outf.write(" MAVLINK_MSG_ID_%s : MAVLink_%s_message,\n" % (
  356. m.name.upper(), m.name.lower()))
  357. outf.write("}\n\n")
  358. t.write(outf, """
  359. class MAVError(Exception):
  360. '''MAVLink error class'''
  361. def __init__(self, msg):
  362. Exception.__init__(self, msg)
  363. self.message = msg
  364. class MAVString(str):
  365. '''NUL terminated string'''
  366. def __init__(self, s):
  367. str.__init__(self)
  368. def __str__(self):
  369. i = self.find(chr(0))
  370. if i == -1:
  371. return self[:]
  372. return self[0:i]
  373. class MAVLink_bad_data(MAVLink_message):
  374. '''
  375. a piece of bad data in a mavlink stream
  376. '''
  377. def __init__(self, data, reason):
  378. MAVLink_message.__init__(self, MAVLINK_MSG_ID_BAD_DATA, 'BAD_DATA')
  379. self._fieldnames = ['data', 'reason']
  380. self.data = data
  381. self.reason = reason
  382. self._msgbuf = data
  383. def __str__(self):
  384. '''Override the __str__ function from MAVLink_messages because non-printable characters are common in to be the reason for this message to exist.'''
  385. return '%s {%s, data:%s}' % (self._type, self.reason, [('%x' % ord(i) if isinstance(i, str) else '%x' % i) for i in self.data])
  386. class MAVLinkSigning(object):
  387. '''MAVLink signing state class'''
  388. def __init__(self):
  389. self.secret_key = None
  390. self.timestamp = 0
  391. self.link_id = 0
  392. self.sign_outgoing = False
  393. self.allow_unsigned_callback = None
  394. self.stream_timestamps = {}
  395. self.sig_count = 0
  396. self.badsig_count = 0
  397. self.goodsig_count = 0
  398. self.unsigned_count = 0
  399. self.reject_count = 0
  400. class MAVLink(object):
  401. '''MAVLink protocol handling class'''
  402. def __init__(self, file, srcSystem=0, srcComponent=0, use_native=False):
  403. self.seq = 0
  404. self.file = file
  405. self.srcSystem = srcSystem
  406. self.srcComponent = srcComponent
  407. self.callback = None
  408. self.callback_args = None
  409. self.callback_kwargs = None
  410. self.send_callback = None
  411. self.send_callback_args = None
  412. self.send_callback_kwargs = None
  413. self.buf = bytearray()
  414. self.buf_index = 0
  415. self.expected_length = HEADER_LEN_V1+2
  416. self.have_prefix_error = False
  417. self.robust_parsing = False
  418. self.protocol_marker = ${protocol_marker}
  419. self.little_endian = ${little_endian}
  420. self.crc_extra = ${crc_extra}
  421. self.sort_fields = ${sort_fields}
  422. self.total_packets_sent = 0
  423. self.total_bytes_sent = 0
  424. self.total_packets_received = 0
  425. self.total_bytes_received = 0
  426. self.total_receive_errors = 0
  427. self.startup_time = time.time()
  428. self.signing = MAVLinkSigning()
  429. if native_supported and (use_native or native_testing or native_force):
  430. print("NOTE: mavnative is currently beta-test code")
  431. self.native = mavnative.NativeConnection(MAVLink_message, mavlink_map)
  432. else:
  433. self.native = None
  434. if native_testing:
  435. self.test_buf = bytearray()
  436. self.mav20_unpacker = struct.Struct('<cBBBBBBHB')
  437. self.mav10_unpacker = struct.Struct('<cBBBBB')
  438. self.mav20_h3_unpacker = struct.Struct('BBB')
  439. self.mav_csum_unpacker = struct.Struct('<H')
  440. self.mav_sign_unpacker = struct.Struct('<IH')
  441. def set_callback(self, callback, *args, **kwargs):
  442. self.callback = callback
  443. self.callback_args = args
  444. self.callback_kwargs = kwargs
  445. def set_send_callback(self, callback, *args, **kwargs):
  446. self.send_callback = callback
  447. self.send_callback_args = args
  448. self.send_callback_kwargs = kwargs
  449. def send(self, mavmsg, force_mavlink1=False):
  450. '''send a MAVLink message'''
  451. buf = mavmsg.pack(self, force_mavlink1=force_mavlink1)
  452. self.file.write(buf)
  453. self.seq = (self.seq + 1) % 256
  454. self.total_packets_sent += 1
  455. self.total_bytes_sent += len(buf)
  456. if self.send_callback:
  457. self.send_callback(mavmsg, *self.send_callback_args, **self.send_callback_kwargs)
  458. def buf_len(self):
  459. return len(self.buf) - self.buf_index
  460. def bytes_needed(self):
  461. '''return number of bytes needed for next parsing stage'''
  462. if self.native:
  463. ret = self.native.expected_length - self.buf_len()
  464. else:
  465. ret = self.expected_length - self.buf_len()
  466. if ret <= 0:
  467. return 1
  468. return ret
  469. def __parse_char_native(self, c):
  470. '''this method exists only to see in profiling results'''
  471. m = self.native.parse_chars(c)
  472. return m
  473. def __callbacks(self, msg):
  474. '''this method exists only to make profiling results easier to read'''
  475. if self.callback:
  476. self.callback(msg, *self.callback_args, **self.callback_kwargs)
  477. def parse_char(self, c):
  478. '''input some data bytes, possibly returning a new message'''
  479. self.buf.extend(c)
  480. self.total_bytes_received += len(c)
  481. if self.native:
  482. if native_testing:
  483. self.test_buf.extend(c)
  484. m = self.__parse_char_native(self.test_buf)
  485. m2 = self.__parse_char_legacy()
  486. if m2 != m:
  487. print("Native: %s\\nLegacy: %s\\n" % (m, m2))
  488. raise Exception('Native vs. Legacy mismatch')
  489. else:
  490. m = self.__parse_char_native(self.buf)
  491. else:
  492. m = self.__parse_char_legacy()
  493. if m is not None:
  494. self.total_packets_received += 1
  495. self.__callbacks(m)
  496. else:
  497. # XXX The idea here is if we've read something and there's nothing left in
  498. # the buffer, reset it to 0 which frees the memory
  499. if self.buf_len() == 0 and self.buf_index != 0:
  500. self.buf = bytearray()
  501. self.buf_index = 0
  502. return m
  503. def __parse_char_legacy(self):
  504. '''input some data bytes, possibly returning a new message (uses no native code)'''
  505. header_len = HEADER_LEN_V1
  506. if self.buf_len() >= 1 and self.buf[self.buf_index] == PROTOCOL_MARKER_V2:
  507. header_len = HEADER_LEN_V2
  508. if self.buf_len() >= 1 and self.buf[self.buf_index] != PROTOCOL_MARKER_V1 and self.buf[self.buf_index] != PROTOCOL_MARKER_V2:
  509. magic = self.buf[self.buf_index]
  510. self.buf_index += 1
  511. if self.robust_parsing:
  512. m = MAVLink_bad_data(bytearray([magic]), 'Bad prefix')
  513. self.expected_length = header_len+2
  514. self.total_receive_errors += 1
  515. return m
  516. if self.have_prefix_error:
  517. return None
  518. self.have_prefix_error = True
  519. self.total_receive_errors += 1
  520. raise MAVError("invalid MAVLink prefix '%s'" % magic)
  521. self.have_prefix_error = False
  522. if self.buf_len() >= 3:
  523. sbuf = self.buf[self.buf_index:3+self.buf_index]
  524. if sys.version_info.major < 3:
  525. sbuf = str(sbuf)
  526. (magic, self.expected_length, incompat_flags) = self.mav20_h3_unpacker.unpack(sbuf)
  527. if magic == PROTOCOL_MARKER_V2 and (incompat_flags & MAVLINK_IFLAG_SIGNED):
  528. self.expected_length += MAVLINK_SIGNATURE_BLOCK_LEN
  529. self.expected_length += header_len + 2
  530. if self.expected_length >= (header_len+2) and self.buf_len() >= self.expected_length:
  531. mbuf = array.array('B', self.buf[self.buf_index:self.buf_index+self.expected_length])
  532. self.buf_index += self.expected_length
  533. self.expected_length = header_len+2
  534. if self.robust_parsing:
  535. try:
  536. if magic == PROTOCOL_MARKER_V2 and (incompat_flags & ~MAVLINK_IFLAG_SIGNED) != 0:
  537. raise MAVError('invalid incompat_flags 0x%x 0x%x %u' % (incompat_flags, magic, self.expected_length))
  538. m = self.decode(mbuf)
  539. except MAVError as reason:
  540. m = MAVLink_bad_data(mbuf, reason.message)
  541. self.total_receive_errors += 1
  542. else:
  543. if magic == PROTOCOL_MARKER_V2 and (incompat_flags & ~MAVLINK_IFLAG_SIGNED) != 0:
  544. raise MAVError('invalid incompat_flags 0x%x 0x%x %u' % (incompat_flags, magic, self.expected_length))
  545. m = self.decode(mbuf)
  546. return m
  547. return None
  548. def parse_buffer(self, s):
  549. '''input some data bytes, possibly returning a list of new messages'''
  550. m = self.parse_char(s)
  551. if m is None:
  552. return None
  553. ret = [m]
  554. while True:
  555. m = self.parse_char("")
  556. if m is None:
  557. return ret
  558. ret.append(m)
  559. return ret
  560. def check_signature(self, msgbuf, srcSystem, srcComponent):
  561. '''check signature on incoming message'''
  562. if isinstance(msgbuf, array.array):
  563. msgbuf = msgbuf.tostring()
  564. timestamp_buf = msgbuf[-12:-6]
  565. link_id = msgbuf[-13]
  566. (tlow, thigh) = self.mav_sign_unpacker.unpack(timestamp_buf)
  567. timestamp = tlow + (thigh<<32)
  568. # see if the timestamp is acceptable
  569. stream_key = (link_id,srcSystem,srcComponent)
  570. if stream_key in self.signing.stream_timestamps:
  571. if timestamp <= self.signing.stream_timestamps[stream_key]:
  572. # reject old timestamp
  573. # print('old timestamp')
  574. return False
  575. else:
  576. # a new stream has appeared. Accept the timestamp if it is at most
  577. # one minute behind our current timestamp
  578. if timestamp + 6000*1000 < self.signing.timestamp:
  579. # print('bad new stream ', timestamp/(100.0*1000*60*60*24*365), self.signing.timestamp/(100.0*1000*60*60*24*365))
  580. return False
  581. self.signing.stream_timestamps[stream_key] = timestamp
  582. # print('new stream')
  583. h = hashlib.new('sha256')
  584. h.update(self.signing.secret_key)
  585. h.update(msgbuf[:-6])
  586. sig1 = str(h.digest())[:6]
  587. sig2 = str(msgbuf)[-6:]
  588. if sig1 != sig2:
  589. # print('sig mismatch')
  590. return False
  591. # the timestamp we next send with is the max of the received timestamp and
  592. # our current timestamp
  593. self.signing.timestamp = max(self.signing.timestamp, timestamp)
  594. return True
  595. def decode(self, msgbuf):
  596. '''decode a buffer as a MAVLink message'''
  597. # decode the header
  598. if msgbuf[0] != PROTOCOL_MARKER_V1:
  599. headerlen = 10
  600. try:
  601. magic, mlen, incompat_flags, compat_flags, seq, srcSystem, srcComponent, msgIdlow, msgIdhigh = self.mav20_unpacker.unpack(msgbuf[:headerlen])
  602. except struct.error as emsg:
  603. raise MAVError('Unable to unpack MAVLink header: %s' % emsg)
  604. msgId = msgIdlow | (msgIdhigh<<16)
  605. mapkey = msgId
  606. else:
  607. headerlen = 6
  608. try:
  609. magic, mlen, seq, srcSystem, srcComponent, msgId = self.mav10_unpacker.unpack(msgbuf[:headerlen])
  610. incompat_flags = 0
  611. compat_flags = 0
  612. except struct.error as emsg:
  613. raise MAVError('Unable to unpack MAVLink header: %s' % emsg)
  614. mapkey = msgId
  615. if (incompat_flags & MAVLINK_IFLAG_SIGNED) != 0:
  616. signature_len = MAVLINK_SIGNATURE_BLOCK_LEN
  617. else:
  618. signature_len = 0
  619. if ord(magic) != PROTOCOL_MARKER_V1 and ord(magic) != PROTOCOL_MARKER_V2:
  620. raise MAVError("invalid MAVLink prefix '%s'" % magic)
  621. if mlen != len(msgbuf)-(headerlen+2+signature_len):
  622. raise MAVError('invalid MAVLink message length. Got %u expected %u, msgId=%u headerlen=%u' % (len(msgbuf)-(headerlen+2+signature_len), mlen, msgId, headerlen))
  623. if not mapkey in mavlink_map:
  624. raise MAVError('unknown MAVLink message ID %s' % str(mapkey))
  625. # decode the payload
  626. type = mavlink_map[mapkey]
  627. fmt = type.format
  628. order_map = type.orders
  629. len_map = type.lengths
  630. crc_extra = type.crc_extra
  631. # decode the checksum
  632. try:
  633. crc, = self.mav_csum_unpacker.unpack(msgbuf[-(2+signature_len):][:2])
  634. except struct.error as emsg:
  635. raise MAVError('Unable to unpack MAVLink CRC: %s' % emsg)
  636. crcbuf = msgbuf[1:-(2+signature_len)]
  637. if ${crc_extra}: # using CRC extra
  638. crcbuf.append(crc_extra)
  639. crc2 = x25crc(crcbuf)
  640. if crc != crc2.crc:
  641. raise MAVError('invalid MAVLink CRC in msgID %u 0x%04x should be 0x%04x' % (msgId, crc, crc2.crc))
  642. sig_ok = False
  643. if signature_len == MAVLINK_SIGNATURE_BLOCK_LEN:
  644. self.signing.sig_count += 1
  645. if self.signing.secret_key is not None:
  646. accept_signature = False
  647. if signature_len == MAVLINK_SIGNATURE_BLOCK_LEN:
  648. sig_ok = self.check_signature(msgbuf, srcSystem, srcComponent)
  649. accept_signature = sig_ok
  650. if sig_ok:
  651. self.signing.goodsig_count += 1
  652. else:
  653. self.signing.badsig_count += 1
  654. if not accept_signature and self.signing.allow_unsigned_callback is not None:
  655. accept_signature = self.signing.allow_unsigned_callback(self, msgId)
  656. if accept_signature:
  657. self.signing.unsigned_count += 1
  658. else:
  659. self.signing.reject_count += 1
  660. elif self.signing.allow_unsigned_callback is not None:
  661. accept_signature = self.signing.allow_unsigned_callback(self, msgId)
  662. if accept_signature:
  663. self.signing.unsigned_count += 1
  664. else:
  665. self.signing.reject_count += 1
  666. if not accept_signature:
  667. raise MAVError('Invalid signature')
  668. csize = type.unpacker.size
  669. mbuf = msgbuf[headerlen:-(2+signature_len)]
  670. if len(mbuf) < csize:
  671. # zero pad to give right size
  672. mbuf.extend([0]*(csize - len(mbuf)))
  673. if len(mbuf) < csize:
  674. raise MAVError('Bad message of type %s length %u needs %s' % (
  675. type, len(mbuf), csize))
  676. mbuf = mbuf[:csize]
  677. try:
  678. t = type.unpacker.unpack(mbuf)
  679. except struct.error as emsg:
  680. raise MAVError('Unable to unpack MAVLink payload type=%s fmt=%s payloadLength=%u: %s' % (
  681. type, fmt, len(mbuf), emsg))
  682. tlist = list(t)
  683. # handle sorted fields
  684. if ${sort_fields}:
  685. t = tlist[:]
  686. if sum(len_map) == len(len_map):
  687. # message has no arrays in it
  688. for i in range(0, len(tlist)):
  689. tlist[i] = t[order_map[i]]
  690. else:
  691. # message has some arrays
  692. tlist = []
  693. for i in range(0, len(order_map)):
  694. order = order_map[i]
  695. L = len_map[order]
  696. tip = sum(len_map[:order])
  697. field = t[tip]
  698. if L == 1 or isinstance(field, str):
  699. tlist.append(field)
  700. else:
  701. tlist.append(t[tip:(tip + L)])
  702. # terminate any strings
  703. for i in range(0, len(tlist)):
  704. if type.fieldtypes[i] == 'char':
  705. if sys.version_info.major >= 3:
  706. tlist[i] = tlist[i].decode('utf-8')
  707. tlist[i] = str(MAVString(tlist[i]))
  708. t = tuple(tlist)
  709. # construct the message object
  710. try:
  711. m = type(*t)
  712. except Exception as emsg:
  713. raise MAVError('Unable to instantiate MAVLink message of type %s : %s' % (type, emsg))
  714. m._signed = sig_ok
  715. if m._signed:
  716. m._link_id = msgbuf[-13]
  717. m._msgbuf = msgbuf
  718. m._payload = msgbuf[6:-(2+signature_len)]
  719. m._crc = crc
  720. m._header = MAVLink_header(msgId, incompat_flags, compat_flags, mlen, seq, srcSystem, srcComponent)
  721. return m
  722. """, xml)
  723. def generate_methods(outf, msgs):
  724. print("Generating methods")
  725. def field_descriptions(fields):
  726. ret = ""
  727. for f in fields:
  728. field_info = ""
  729. if f.units:
  730. field_info += "%s " % f.units
  731. field_info += "(type:%s" % f.type
  732. if f.enum:
  733. field_info += ", values:%s" % f.enum
  734. field_info += ")"
  735. ret += " %-18s : %s %s\n" % (f.name, f.description.strip(), field_info)
  736. return ret
  737. wrapper = textwrap.TextWrapper(initial_indent="", subsequent_indent=" ")
  738. for m in msgs:
  739. comment = "%s\n\n%s" % (wrapper.fill(m.description.strip()), field_descriptions(m.fields))
  740. selffieldnames = 'self, '
  741. for i in range(len(m.fields)):
  742. f = m.fields[i]
  743. if f.omit_arg:
  744. selffieldnames += '%s=%s, ' % (f.name, f.const_value)
  745. elif m.extensions_start is not None and i >= m.extensions_start:
  746. fdefault = m.fielddefaults[i]
  747. selffieldnames += "%s=%s, " % (f.name, fdefault)
  748. else:
  749. selffieldnames += '%s, ' % f.name
  750. selffieldnames = selffieldnames[:-2]
  751. sub = {'NAMELOWER': m.name.lower(),
  752. 'SELFFIELDNAMES': selffieldnames,
  753. 'COMMENT': comment,
  754. 'FIELDNAMES': ", ".join(m.fieldnames)}
  755. t.write(outf, """
  756. def ${NAMELOWER}_encode(${SELFFIELDNAMES}):
  757. '''
  758. ${COMMENT}
  759. '''
  760. return MAVLink_${NAMELOWER}_message(${FIELDNAMES})
  761. """, sub)
  762. t.write(outf, """
  763. def ${NAMELOWER}_send(${SELFFIELDNAMES}, force_mavlink1=False):
  764. '''
  765. ${COMMENT}
  766. '''
  767. return self.send(self.${NAMELOWER}_encode(${FIELDNAMES}), force_mavlink1=force_mavlink1)
  768. """, sub)
  769. def generate(basename, xml):
  770. '''generate complete python implementation'''
  771. if basename.endswith('.py'):
  772. filename = basename
  773. else:
  774. filename = basename + '.py'
  775. msgs = []
  776. enums = []
  777. filelist = []
  778. for x in xml:
  779. msgs.extend(x.message)
  780. enums.extend(x.enum)
  781. filelist.append(os.path.basename(x.filename))
  782. for m in msgs:
  783. m.fielddefaults = []
  784. if xml[0].little_endian:
  785. m.fmtstr = '<'
  786. else:
  787. m.fmtstr = '>'
  788. m.native_fmtstr = m.fmtstr
  789. for f in m.ordered_fields:
  790. m.fmtstr += mavfmt(f)
  791. m.fielddefaults.append(mavdefault(f))
  792. m.native_fmtstr += native_mavfmt(f)
  793. m.order_map = [0] * len(m.fieldnames)
  794. m.len_map = [0] * len(m.fieldnames)
  795. m.array_len_map = [0] * len(m.fieldnames)
  796. for i in range(0, len(m.fieldnames)):
  797. m.order_map[i] = m.ordered_fieldnames.index(m.fieldnames[i])
  798. m.array_len_map[i] = m.ordered_fields[i].array_length
  799. for i in range(0, len(m.fieldnames)):
  800. n = m.order_map[i]
  801. m.len_map[n] = m.fieldlengths[i]
  802. print("Generating %s" % filename)
  803. outf = open(filename, "w")
  804. generate_preamble(outf, msgs, basename, filelist, xml[0])
  805. generate_enums(outf, enums)
  806. generate_message_ids(outf, msgs)
  807. generate_classes(outf, msgs)
  808. generate_mavlink_class(outf, msgs, xml[0])
  809. generate_methods(outf, msgs)
  810. outf.close()
  811. print("Generated %s OK" % filename)