mavgen_java.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. #!/usr/bin/env python
  2. '''
  3. Parse a MAVLink protocol XML file and generate a Java 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. from builtins import object
  10. import os
  11. from . import mavparse, mavtemplate
  12. t = mavtemplate.MAVTemplate()
  13. def generate_enums(basename, xml):
  14. '''generate main header per XML file'''
  15. directory = os.path.join(basename, '''enums''')
  16. mavparse.mkdir_p(directory)
  17. for en in xml.enum:
  18. f = open(os.path.join(directory, en.name+".java"), mode='w')
  19. t.write(f, '''
  20. /* AUTO-GENERATED FILE. DO NOT MODIFY.
  21. *
  22. * This class was automatically generated by the
  23. * java mavlink generator tool. It should not be modified by hand.
  24. */
  25. package com.MAVLink.enums;
  26. /**
  27. * ${description}
  28. */
  29. public class ${name} {
  30. ${{entry: public static final int ${name} = ${value}; /* ${description} |${{param:${description}| }} */
  31. }}
  32. }
  33. ''', en)
  34. f.close()
  35. def generate_CRC(directory, xml):
  36. # and message CRCs array
  37. xml.message_crcs_array = ''
  38. for msgid in range(256):
  39. crc = xml.message_crcs.get(msgid, 0)
  40. xml.message_crcs_array += '%u, ' % crc
  41. xml.message_crcs_array = xml.message_crcs_array[:-2]
  42. f = open(os.path.join(directory, "CRC.java"), mode='w')
  43. t.write(f,'''
  44. /* AUTO-GENERATED FILE. DO NOT MODIFY.
  45. *
  46. * This class was automatically generated by the
  47. * java mavlink generator tool. It should not be modified by hand.
  48. */
  49. package com.MAVLink.${basename};
  50. /**
  51. * X.25 CRC calculation for MAVlink messages. The checksum must be initialized,
  52. * updated with witch field of the message, and then finished with the message
  53. * id.
  54. *
  55. */
  56. public class CRC {
  57. private static final int[] MAVLINK_MESSAGE_CRCS = {${message_crcs_array}};
  58. private static final int CRC_INIT_VALUE = 0xffff;
  59. private int crcValue;
  60. /**
  61. * Accumulate the X.25 CRC by adding one char at a time.
  62. *
  63. * The checksum function adds the hash of one char at a time to the 16 bit
  64. * checksum (uint16_t).
  65. *
  66. * @param data
  67. * new char to hash
  68. **/
  69. public void update_checksum(int data) {
  70. data = data & 0xff; //cast because we want an unsigned type
  71. int tmp = data ^ (crcValue & 0xff);
  72. tmp ^= (tmp << 4) & 0xff;
  73. crcValue = ((crcValue >> 8) & 0xff) ^ (tmp << 8) ^ (tmp << 3) ^ ((tmp >> 4) & 0xf);
  74. }
  75. /**
  76. * Finish the CRC calculation of a message, by running the CRC with the
  77. * Magic Byte. This Magic byte has been defined in MAVlink v1.0.
  78. *
  79. * @param msgid
  80. * The message id number
  81. */
  82. public void finish_checksum(int msgid) {
  83. update_checksum(MAVLINK_MESSAGE_CRCS[msgid]);
  84. }
  85. /**
  86. * Initialize the buffer for the X.25 CRC
  87. *
  88. */
  89. public void start_checksum() {
  90. crcValue = CRC_INIT_VALUE;
  91. }
  92. public int getMSB() {
  93. return ((crcValue >> 8) & 0xff);
  94. }
  95. public int getLSB() {
  96. return (crcValue & 0xff);
  97. }
  98. public CRC() {
  99. start_checksum();
  100. }
  101. }
  102. ''',xml)
  103. f.close()
  104. def generate_message_h(directory, m):
  105. '''generate per-message header for a XML file'''
  106. f = open(os.path.join(directory, 'msg_%s.java' % m.name_lower), mode='w')
  107. (path_head, path_tail) = os.path.split(directory)
  108. if path_tail == "":
  109. (path_head, path_tail) = os.path.split(path_head)
  110. t.write(f, '''
  111. /* AUTO-GENERATED FILE. DO NOT MODIFY.
  112. *
  113. * This class was automatically generated by the
  114. * java mavlink generator tool. It should not be modified by hand.
  115. */
  116. // MESSAGE ${name} PACKING
  117. package com.MAVLink.%s;
  118. import com.MAVLink.MAVLinkPacket;
  119. import com.MAVLink.Messages.MAVLinkMessage;
  120. import com.MAVLink.Messages.MAVLinkPayload;
  121. /**
  122. * ${description}
  123. */
  124. public class msg_${name_lower} extends MAVLinkMessage{
  125. public static final int MAVLINK_MSG_ID_${name} = ${id};
  126. public static final int MAVLINK_MSG_LENGTH = ${wire_length};
  127. private static final long serialVersionUID = MAVLINK_MSG_ID_${name};
  128. ${{ordered_fields:
  129. /**
  130. * ${description}
  131. */
  132. public ${type} ${name}${array_suffix};
  133. }}
  134. /**
  135. * Generates the payload for a mavlink message for a message of this type
  136. * @return
  137. */
  138. public MAVLinkPacket pack(){
  139. MAVLinkPacket packet = new MAVLinkPacket(MAVLINK_MSG_LENGTH);
  140. packet.sysid = 255;
  141. packet.compid = 190;
  142. packet.msgid = MAVLINK_MSG_ID_${name};
  143. ${{ordered_fields:
  144. ${packField}
  145. }}
  146. return packet;
  147. }
  148. /**
  149. * Decode a ${name_lower} message into this class fields
  150. *
  151. * @param payload The message to decode
  152. */
  153. public void unpack(MAVLinkPayload payload) {
  154. payload.resetIndex();
  155. ${{ordered_fields:
  156. ${unpackField}
  157. }}
  158. }
  159. /**
  160. * Constructor for a new message, just initializes the msgid
  161. */
  162. public msg_${name_lower}(){
  163. msgid = MAVLINK_MSG_ID_${name};
  164. }
  165. /**
  166. * Constructor for a new message, initializes the message with the payload
  167. * from a mavlink packet
  168. *
  169. */
  170. public msg_${name_lower}(MAVLinkPacket mavLinkPacket){
  171. this.sysid = mavLinkPacket.sysid;
  172. this.compid = mavLinkPacket.compid;
  173. this.msgid = MAVLINK_MSG_ID_${name};
  174. unpack(mavLinkPacket.payload);
  175. }
  176. ${{ordered_fields: ${getText} }}
  177. /**
  178. * Returns a string with the MSG name and data
  179. */
  180. public String toString(){
  181. return "MAVLINK_MSG_ID_${name} - sysid:"+sysid+" compid:"+compid+${{ordered_fields:" ${name}:"+${name}+}}"";
  182. }
  183. }
  184. ''' % path_tail, m)
  185. f.close()
  186. def generate_MAVLinkMessage(directory, xml_list):
  187. f = open(os.path.join(directory, "MAVLinkPacket.java"), mode='w')
  188. imports = []
  189. for xml in xml_list:
  190. importString = "import com.MAVLink.{}.*;".format(xml.basename)
  191. imports.append(importString)
  192. xml_list[0].importString = os.linesep.join(imports)
  193. t.write(f, '''
  194. /* AUTO-GENERATED FILE. DO NOT MODIFY.
  195. *
  196. * This class was automatically generated by the
  197. * java mavlink generator tool. It should not be modified by hand.
  198. */
  199. package com.MAVLink;
  200. import java.io.Serializable;
  201. import com.MAVLink.Messages.MAVLinkPayload;
  202. import com.MAVLink.Messages.MAVLinkMessage;
  203. import com.MAVLink.${basename}.CRC;
  204. ${importString}
  205. /**
  206. * Common interface for all MAVLink Messages
  207. * Packet Anatomy
  208. * This is the anatomy of one packet. It is inspired by the CAN and SAE AS-4 standards.
  209. * Byte Index Content Value Explanation
  210. * 0 Packet start sign v1.0: 0xFE Indicates the start of a new packet. (v0.9: 0x55)
  211. * 1 Payload length 0 - 255 Indicates length of the following payload.
  212. * 2 Packet sequence 0 - 255 Each component counts up his send sequence. Allows to detect packet loss
  213. * 3 System ID 1 - 255 ID of the SENDING system. Allows to differentiate different MAVs on the same network.
  214. * 4 Component ID 0 - 255 ID of the SENDING component. Allows to differentiate different components of the same system, e.g. the IMU and the autopilot.
  215. * 5 Message ID 0 - 255 ID of the message - the id defines what the payload means and how it should be correctly decoded.
  216. * 6 to (n+6) Payload 0 - 255 Data of the message, depends on the message id.
  217. * (n+7)to(n+8) Checksum (low byte, high byte) ITU X.25/SAE AS-4 hash, excluding packet start sign, so bytes 1..(n+6) Note: The checksum also includes MAVLINK_CRC_EXTRA (Number computed from message fields. Protects the packet from decoding a different version of the same packet but with different variables).
  218. * The checksum is the same as used in ITU X.25 and SAE AS-4 standards (CRC-16-CCITT), documented in SAE AS5669A. Please see the MAVLink source code for a documented C-implementation of it. LINK TO CHECKSUM
  219. * The minimum packet length is 8 bytes for acknowledgement packets without payload
  220. * The maximum packet length is 263 bytes for full payload
  221. *
  222. */
  223. public class MAVLinkPacket implements Serializable {
  224. private static final long serialVersionUID = 2095947771227815314L;
  225. public static final int MAVLINK_STX = 254;
  226. /**
  227. * Message length. NOT counting STX, LENGTH, SEQ, SYSID, COMPID, MSGID, CRC1 and CRC2
  228. */
  229. public final int len;
  230. /**
  231. * Message sequence
  232. */
  233. public int seq;
  234. /**
  235. * ID of the SENDING system. Allows to differentiate different MAVs on the
  236. * same network.
  237. */
  238. public int sysid;
  239. /**
  240. * ID of the SENDING component. Allows to differentiate different components
  241. * of the same system, e.g. the IMU and the autopilot.
  242. */
  243. public int compid;
  244. /**
  245. * ID of the message - the id defines what the payload means and how it
  246. * should be correctly decoded.
  247. */
  248. public int msgid;
  249. /**
  250. * Data of the message, depends on the message id.
  251. */
  252. public MAVLinkPayload payload;
  253. /**
  254. * ITU X.25/SAE AS-4 hash, excluding packet start sign, so bytes 1..(n+6)
  255. * Note: The checksum also includes MAVLINK_CRC_EXTRA (Number computed from
  256. * message fields. Protects the packet from decoding a different version of
  257. * the same packet but with different variables).
  258. */
  259. public CRC crc;
  260. public MAVLinkPacket(int payloadLength){
  261. len = payloadLength;
  262. payload = new MAVLinkPayload(payloadLength);
  263. }
  264. /**
  265. * Check if the size of the Payload is equal to the "len" byte
  266. */
  267. public boolean payloadIsFilled() {
  268. return payload.size() >= len;
  269. }
  270. /**
  271. * Update CRC for this packet.
  272. */
  273. public void generateCRC(){
  274. if(crc == null){
  275. crc = new CRC();
  276. }
  277. else{
  278. crc.start_checksum();
  279. }
  280. crc.update_checksum(len);
  281. crc.update_checksum(seq);
  282. crc.update_checksum(sysid);
  283. crc.update_checksum(compid);
  284. crc.update_checksum(msgid);
  285. payload.resetIndex();
  286. final int payloadSize = payload.size();
  287. for (int i = 0; i < payloadSize; i++) {
  288. crc.update_checksum(payload.getByte());
  289. }
  290. crc.finish_checksum(msgid);
  291. }
  292. /**
  293. * Encode this packet for transmission.
  294. *
  295. * @return Array with bytes to be transmitted
  296. */
  297. public byte[] encodePacket() {
  298. byte[] buffer = new byte[6 + len + 2];
  299. int i = 0;
  300. buffer[i++] = (byte) MAVLINK_STX;
  301. buffer[i++] = (byte) len;
  302. buffer[i++] = (byte) seq;
  303. buffer[i++] = (byte) sysid;
  304. buffer[i++] = (byte) compid;
  305. buffer[i++] = (byte) msgid;
  306. final int payloadSize = payload.size();
  307. for (int j = 0; j < payloadSize; j++) {
  308. buffer[i++] = payload.payload.get(j);
  309. }
  310. generateCRC();
  311. buffer[i++] = (byte) (crc.getLSB());
  312. buffer[i++] = (byte) (crc.getMSB());
  313. return buffer;
  314. }
  315. /**
  316. * Unpack the data in this packet and return a MAVLink message
  317. *
  318. * @return MAVLink message decoded from this packet
  319. */
  320. public MAVLinkMessage unpack() {
  321. switch (msgid) {
  322. ''', xml_list[0])
  323. for xml in xml_list:
  324. t.write(f, '''
  325. ${{message:
  326. case msg_${name_lower}.MAVLINK_MSG_ID_${name}:
  327. return new msg_${name_lower}(this);
  328. }}
  329. ''',xml)
  330. f.write('''
  331. default:
  332. return null;
  333. }
  334. }
  335. }
  336. ''')
  337. f.close()
  338. def copy_fixed_headers(directory, xml):
  339. '''copy the fixed protocol headers to the target directory'''
  340. import shutil
  341. hlist = [ 'Parser.java', 'Messages/MAVLinkMessage.java', 'Messages/MAVLinkPayload.java', 'Messages/MAVLinkStats.java' ]
  342. basepath = os.path.dirname(os.path.realpath(__file__))
  343. srcpath = os.path.join(basepath, 'java/lib')
  344. print("Copying fixed headers")
  345. for h in hlist:
  346. src = os.path.realpath(os.path.join(srcpath, h))
  347. dest = os.path.realpath(os.path.join(directory, h))
  348. if src == dest:
  349. continue
  350. destdir = os.path.realpath(os.path.join(directory, 'Messages'))
  351. try:
  352. os.makedirs(destdir)
  353. except:
  354. print("Not re-creating Messages directory")
  355. shutil.copy(src, dest)
  356. class mav_include(object):
  357. def __init__(self, base):
  358. self.base = base
  359. def mavfmt(field, typeInfo=False):
  360. '''work out the struct format for a type'''
  361. map = {
  362. 'float' : ('float', 'Float'),
  363. 'double' : ('double', 'Double'),
  364. 'char' : ('byte', 'Byte'),
  365. 'int8_t' : ('byte', 'Byte'),
  366. 'uint8_t' : ('short', 'UnsignedByte'),
  367. 'uint8_t_mavlink_version' : ('short', 'UnsignedByte'),
  368. 'int16_t' : ('short', 'Short'),
  369. 'uint16_t' : ('int', 'UnsignedShort'),
  370. 'int32_t' : ('int', 'Int'),
  371. 'uint32_t' : ('long', 'UnsignedInt'),
  372. 'int64_t' : ('long', 'Long'),
  373. 'uint64_t' : ('long', 'UnsignedLong'),
  374. }
  375. if typeInfo:
  376. return map[field.type][1]
  377. else:
  378. return map[field.type][0]
  379. def generate_one(basename, xml):
  380. '''generate headers for one XML file'''
  381. directory = os.path.join(basename, xml.basename)
  382. print("Generating Java implementation in directory %s" % directory)
  383. mavparse.mkdir_p(directory)
  384. if xml.little_endian:
  385. xml.mavlink_endian = "MAVLINK_LITTLE_ENDIAN"
  386. else:
  387. xml.mavlink_endian = "MAVLINK_BIG_ENDIAN"
  388. if xml.crc_extra:
  389. xml.crc_extra_define = "1"
  390. else:
  391. xml.crc_extra_define = "0"
  392. if xml.sort_fields:
  393. xml.aligned_fields_define = "1"
  394. else:
  395. xml.aligned_fields_define = "0"
  396. # work out the included headers
  397. xml.include_list = []
  398. for i in xml.include:
  399. base = i[:-4]
  400. xml.include_list.append(mav_include(base))
  401. # form message lengths array
  402. xml.message_lengths_array = ''
  403. for mlen in xml.message_lengths:
  404. xml.message_lengths_array += '%u, ' % mlen
  405. xml.message_lengths_array = xml.message_lengths_array[:-2]
  406. # form message info array
  407. xml.message_info_array = ''
  408. for name in xml.message_names:
  409. if name is not None:
  410. xml.message_info_array += 'MAVLINK_MESSAGE_INFO_%s, ' % name
  411. else:
  412. # Several C compilers don't accept {NULL} for
  413. # multi-dimensional arrays and structs
  414. # feed the compiler a "filled" empty message
  415. xml.message_info_array += '{"EMPTY",0,{{"","",MAVLINK_TYPE_CHAR,0,0,0}}}, '
  416. xml.message_info_array = xml.message_info_array[:-2]
  417. # add some extra field attributes for convenience with arrays
  418. for m in xml.message:
  419. m.msg_name = m.name
  420. if xml.crc_extra:
  421. m.crc_extra_arg = ", %s" % m.crc_extra
  422. else:
  423. m.crc_extra_arg = ""
  424. for f in m.fields:
  425. if f.print_format is None:
  426. f.c_print_format = 'NULL'
  427. else:
  428. f.c_print_format = '"%s"' % f.print_format
  429. f.getText = ''
  430. if f.array_length != 0:
  431. f.array_suffix = '[] = new %s[%u]' % (mavfmt(f),f.array_length)
  432. f.array_prefix = '*'
  433. f.array_tag = '_array'
  434. f.array_arg = ', %u' % f.array_length
  435. f.array_return_arg = '%s, %u, ' % (f.name, f.array_length)
  436. f.array_const = 'const '
  437. f.decode_left = ''
  438. f.decode_right = 'm.%s' % (f.name)
  439. f.unpackField = '''
  440. for (int i = 0; i < this.%s.length; i++) {
  441. this.%s[i] = payload.get%s();
  442. }
  443. ''' % (f.name, f.name, mavfmt(f, True) )
  444. f.packField = '''
  445. for (int i = 0; i < %s.length; i++) {
  446. packet.payload.put%s(%s[i]);
  447. }
  448. ''' % (f.name, mavfmt(f, True),f.name)
  449. f.return_type = 'uint16_t'
  450. f.get_arg = ', %s *%s' % (f.type, f.name)
  451. if f.type == 'char':
  452. f.c_test_value = '"%s"' % f.test_value
  453. f.getText = '''
  454. /**
  455. * Sets the buffer of this message with a string, adds the necessary padding
  456. */
  457. public void set%s(String str) {
  458. int len = Math.min(str.length(), %d);
  459. for (int i=0; i<len; i++) {
  460. %s[i] = (byte) str.charAt(i);
  461. }
  462. for (int i=len; i<%d; i++) { // padding for the rest of the buffer
  463. %s[i] = 0;
  464. }
  465. }
  466. /**
  467. * Gets the message, formated as a string
  468. */
  469. public String get%s() {
  470. StringBuffer buf = new StringBuffer();
  471. for (int i = 0; i < %d; i++) {
  472. if (%s[i] != 0)
  473. buf.append((char) %s[i]);
  474. else
  475. break;
  476. }
  477. return buf.toString();
  478. }
  479. ''' % (f.name.title(),f.array_length,f.name,f.array_length,f.name,f.name.title(),f.array_length,f.name,f.name)
  480. else:
  481. test_strings = []
  482. for v in f.test_value:
  483. test_strings.append(str(v))
  484. f.c_test_value = '{ %s }' % ', '.join(test_strings)
  485. else:
  486. f.array_suffix = ''
  487. f.array_prefix = ''
  488. f.array_tag = ''
  489. f.array_arg = ''
  490. f.array_return_arg = ''
  491. f.array_const = ''
  492. f.decode_left = '%s' % (f.name)
  493. f.decode_right = ''
  494. f.unpackField = 'this.%s = payload.get%s();' % (f.name, mavfmt(f, True))
  495. f.packField = 'packet.payload.put%s(%s);' % (mavfmt(f, True),f.name)
  496. f.get_arg = ''
  497. f.return_type = f.type
  498. if f.type == 'char':
  499. f.c_test_value = "'%s'" % f.test_value
  500. elif f.type == 'uint64_t':
  501. f.c_test_value = "%sULL" % f.test_value
  502. elif f.type == 'int64_t':
  503. f.c_test_value = "%sLL" % f.test_value
  504. else:
  505. f.c_test_value = f.test_value
  506. # cope with uint8_t_mavlink_version
  507. for m in xml.message:
  508. m.arg_fields = []
  509. m.array_fields = []
  510. m.scalar_fields = []
  511. for f in m.ordered_fields:
  512. if f.array_length != 0:
  513. m.array_fields.append(f)
  514. else:
  515. m.scalar_fields.append(f)
  516. for f in m.fields:
  517. if not f.omit_arg:
  518. m.arg_fields.append(f)
  519. f.putname = f.name
  520. else:
  521. f.putname = f.const_value
  522. # fix types to java
  523. for m in xml.message:
  524. for f in m.ordered_fields:
  525. f.type = mavfmt(f)
  526. generate_CRC(directory, xml)
  527. for m in xml.message:
  528. generate_message_h(directory, m)
  529. def generate(basename, xml_list):
  530. '''generate complete MAVLink Java implemenation'''
  531. for xml in xml_list:
  532. generate_one(basename, xml)
  533. generate_enums(basename, xml)
  534. generate_MAVLinkMessage(basename, xml_list)
  535. copy_fixed_headers(basename, xml_list[0])