mavlink_gitbook.py
Go to the documentation of this file.
1 #! /usr/bin/python
2 
3 """
4 This script generates markdown files for all the MAVLink message definition XML at:
5 https://github.com/mavlink/mavlink/tree/master/message_definitions/v1.0
6 
7 The files can be imported into a gitbook to display the messages as HTML
8 
9 The script runs on both Python2 and Python 3. The following libraries must be imported: lxml, requests, bs4.
10 
11 The file is run in mavlink/doc/ with no arguments. It writes the files to /messages/
12 """
13 
14 import lxml.etree as ET
15 import requests
16 from bs4 import BeautifulSoup as bs
17 import re
18 import os # for walk
19 
20 
21 xsl_file_name = "mavlink_to_html_table_gitbook.xsl"
22 xml_message_definitions_dir_name = "../message_definitions/v1.0/"
23 
24 output_dir = "./messages/"
25 output_dir_html=output_dir+"_html/"
26 if not os.path.exists(output_dir_html):
27  os.makedirs(output_dir_html)
28 
29 
30 # File for index
31 index_file_name = "README.md"
32 index_file_name = output_dir + index_file_name
33 
34 # Get XSLT
35 with open(xsl_file_name, 'r') as content_file:
36  xsl_file = content_file.read()
37 xslt = ET.fromstring(xsl_file)
38 
39 #initialise text for index file.
40 index_text="""<!-- THIS FILE IS AUTO-GENERATED (DO NOT UPDATE GITBOOK): https://github.com/mavlink/mavlink/blob/master/doc/mavlink_gitbook.py -->
41 # Dialects {#dialects}
42 
43 MAVLink *dialects* are XML files that define *protocol-* and *vendor-specific* messages, enums and commands.
44 
45 Dialects may *include* other MAVLink XML files.
46 A typical pattern is for a dialect to include [common.xml](../messages/common.md) (containing the *MAVLink standard definitions*), extending it with vendor or protocol specific messages.
47 While a dialect can include any other message definition, only only a single level of nesting is supported ([at time of writing](https://github.com/ArduPilot/pymavlink/pull/248)).
48 
49 > **Note** Vendor forks of MAVLink may contain dialect messages that are not yet merged, and hence will not appear in this documentation.
50 
51 The dialect files are stored alongside in separate XML files in [mavlink/message definitions](https://github.com/mavlink/mavlink/blob/master/message_definitions/).
52 
53 The human-readable forms of the XML dialect files are linked below:
54 """
55 
56 #Fix up the BeautifulSoup output so to fix build-link errors in the generated gitbook.
57 ## BS puts each tag/content in its own line. Gitbook generates anchors using the spaces/newlines.
58 ## This puts displayed text content immediately within tags so that anchors/links generate properly
59 def fix_content_in_tags(input_html):
60  #print("fix_content_in_tags was called")
61  def remove_space_between_content_tags(matchobj):
62  stripped_string=matchobj.group(1).strip()
63  return '>%s<' % stripped_string
64 
65  input_html=re.sub(r'>(\s+?\w+?.*?)<', remove_space_between_content_tags, input_html,flags=re.DOTALL)
66  return input_html
67 
69  ## Fixes up file extension .xml.md.unlikely (easier than fixing up the XSLT to strip file extensions!)
70  input_html=input_html.replace('.xml.md.unlikely','.md')
71  return input_html
72 
73 def fix_replace_space_marker(input_html):
74  ## Above we remove hidden space. I can't seem to regexp just that type of space, so use space markers in text
75  input_html=input_html.replace('xxx_space_xxx',' ')
76  return input_html
77 
78 
79 def strip_text_before_string(original_text,strip_text):
80  # Strip out all text before some string
81  index=original_text.find(strip_text)
82  stripped_string=original_text
83  if index !=-1 :
84  stripped_string = stripped_string[index:]
85  return stripped_string
86 
87 def inject_top_level_docs(input_html,filename):
88  #Inject top level heading and other details.
89  print('FILENAME: %s' % filename)
90  insert_text='<!-- THIS FILE IS AUTO-GENERATED: https://github.com/mavlink/mavlink/blob/master/doc/mavlink_gitbook.py -->'
91  if filename == 'common.xml':
92  insert_text+="""
93 # MAVLINK Common Message Set
94 
95 The MAVLink *common* message set is defined in [common.xml](https://github.com/mavlink/mavlink/blob/master/message_definitions/v1.0/common.xml).
96 It contains the *standard* definitions that are managed by the MAVLink project.
97 
98 The definitions cover functionality that is considered useful to most ground control stations and autopilots.
99 MAVLink-compatible systems are expected to use these definitions where possible (if an appropriate message exists) rather than rolling out variants in their own [dialects](../messages/README.md).
100 
101 This topic is a human-readable form of [common.xml](https://github.com/mavlink/mavlink/blob/master/message_definitions/v1.0/common.xml).
102 """
103  elif filename == 'ardupilotmega.xml':
104  insert_text+="""
105 # Dialect: ArduPilotMega
106 
107 These messages define the ArduPilot specific message set, which is custom to [http://ardupilot.org](http://ardupilot.org).
108 
109 This topic is a human-readable form of the XML definition file: [ardupilotmega.xml](https://github.com/mavlink/mavlink/blob/master/message_definitions/v1.0/ardupilotmega.xml).
110 
111 > **Warning** The ArduPilot MAVLink fork of [ardupilotmega.xml](https://github.com/ArduPilot/mavlink/blob/master/message_definitions/v1.0/ardupilotmega.xml) may contain messages that have not yet been merged into this documentation.
112 """
113  else:
114  insert_text+='\n# Dialect: %s' % filename.rsplit('.',1)[0]
115  insert_text+='\n\n*This is a human-readable form of the XML definition file: [%s](https://github.com/mavlink/mavlink/blob/master/message_definitions/v1.0/%s).*' % (filename, filename)
116  insert_text+="""
117 
118 <span></span>
119 > **Note** MAVLink 2 messages have an ID > 255 and are marked up using **(MAVLink 2)** in their description.
120 
121 <span id="mav2_extension_field"></span>
122 > **Note** MAVLink 2 extension fields that have been added to MAVLink 1 messages are displayed in blue.
123 
124 <style>
125 td {
126  vertical-align:top;
127 }
128 </style>
129 """
130  # Include HTML in generated content
131  insert_text+='\n\n{%% include "_html/%s.html" %%}' % filename[:-4]
132  input_html=insert_text+'\n\n'+input_html
133 
134  #print(input_html)
135  return input_html
136 
137 dialect_files = set()
138 
139 for subdir, dirs, files in os.walk(xml_message_definitions_dir_name):
140  for file in files:
141  print(file)
142  if not file.endswith('.xml'): #only process xml files.
143  continue
144  xml_file_name = xml_message_definitions_dir_name+file
145  with open(xml_file_name, 'r') as content_file:
146  xml_file = content_file.read()
147  dom = ET.fromstring(xml_file)
148  transform = ET.XSLT(xslt)
149  newdom = transform(dom)
150 
151  #Prettify the HTML using BeautifulSoup
152  soup=bs(str(newdom), "lxml")
153  prettyHTML=soup.prettify()
154 
155  #Strip out text before <html> tag in XSLT output
156  prettyHTML=strip_text_before_string(prettyHTML,'<html>')
157  prettyHTML = fix_content_in_tags(prettyHTML)
158 
159  #Replace invalid file extensions (workaround for xslt)
160  prettyHTML = fix_include_file_extension(prettyHTML)
161 
162  #Replace space markers with intentional space
163  prettyHTML = fix_replace_space_marker(prettyHTML)
164 
165  #Write output html file
166  output_file_name_html = file.rsplit('.',1)[0]+".html"
167 
168  output_file_name_html_withdir = output_dir_html+output_file_name_html
169  print("Output filename (html): %s" % output_file_name_html)
170 
171  with open(output_file_name_html_withdir, 'w') as out:
172  out.write(prettyHTML)
173 
174 
175  #Write output markdown file
176  output_file_name_prefix = file.rsplit('.',1)[0]
177 
178  markdown_text=''
179  #Inject a heading and doc-type intro (markdown format)
180  markdown_text = inject_top_level_docs(markdown_text,file)
181 
182  output_file_name_md_withdir = output_dir+output_file_name_prefix+'.md'
183  print("Output filename (md): %s" % output_file_name_md_withdir)
184 
185  with open(output_file_name_md_withdir, 'w') as out:
186  out.write(markdown_text)
187 
188  # Create sortable list of output file names
189  if not file=='common.xml':
190  dialect_files.add(output_file_name_prefix)
191 
192 for the_file in sorted(dialect_files):
193  index_text+='\n* [%s.xml](%s.md)' % (the_file,the_file)
194 
195 #Write the index
196 with open(index_file_name, 'w') as content_file:
197  content_file.write(index_text)
198 
199 print("COMPLETED")
200 
201 
202 


mavlink
Author(s): Lorenz Meier
autogenerated on Sun Apr 7 2019 02:06:02