#! /usr/bin/python """ This script generates markdown files for all the MAVLink message definition XML at: https://github.com/mavlink/mavlink/tree/master/message_definitions/v1.0 The files can be imported into a gitbook to display the messages as HTML The script runs on both Python2 and Python 3. The following libraries must be imported: lxml, requests, bs4. The file is run in mavlink/doc/ with no arguments. It writes the files to /messages/ """ import lxml.etree as ET import requests from bs4 import BeautifulSoup as bs import re import os # for walk xsl_file_name = "mavlink_to_html_table_gitbook.xsl" xml_message_definitions_dir_name = "../message_definitions/v1.0/" output_dir = "./messages/" output_dir_html=output_dir+"_html/" if not os.path.exists(output_dir_html): os.makedirs(output_dir_html) # File for index index_file_name = "README.md" index_file_name = output_dir + index_file_name # Get XSLT with open(xsl_file_name, 'r') as content_file: xsl_file = content_file.read() xslt = ET.fromstring(xsl_file) #initialise text for index file. index_text=""" # XML Definition Files & Dialects MAVLink definitions files can be found in [mavlink/message definitions](https://github.com/mavlink/mavlink/blob/master/message_definitions/). These can roughly be divided into: - [Standard definitions](#standard-definitions) - core definitions shared by many flight stacks - [Test definitions](#test-definitions) - definitions to support testing and validation - [Dialects](#dialects) - *protocol-* and *vendor-specific* messages, enums and commands ## Standard Definitions The following XML definition files are considered standard/core (i.e. not dialects): - [minimal.xml](minimal.md) - the minimum set of entities (messages, enums, MAV_CMD) required to set up a MAVLink network. - [standard.xml](standard.md) - the standard set of entities that are implemented by almost all flight stacks (at least 2, in a compatible way). This `includes` [minimal.xml](minimal.md). - [common.xml](../messages/common.md) - the set of entities that have been implemented in at least one core flight stack. This `includes` [standard.xml](minimal.md) > **Note** We are still working towards moving the truly standard entities from **common.xml** to **standard.xml** Currently you should include [common.xml](../messages/common.md) In addition: - [development.xml](development.md) - XML definitions that are _proposed_ for inclusion in the standard definitions. These are work in progress. ## Test Definitions The following definitions are used for testing and dialect validation: - [all.xml](all.md) - This includes all other XML files, and is used to verify that there are no ID clashes (and can potentially be used by GCS to communicate with any core dialect). - [test.xml](test.md) - Test XML definition file. ## Dialects {#dialects} MAVLink *dialects* are XML definition files that define *protocol-* and *vendor-specific* messages, enums and commands. > **Note** Vendor forks of MAVLink may contain XML entities that have not yet been pushed into the main repository (and will not be documented). Dialects may *include* other MAVLink XML files, which may in turn contain other XML files (up to 5 levels of XML file nesting are allowed - see `MAXIMUM_INCLUDE_FILE_NESTING` in [mavgen.py](https://github.com/ArduPilot/pymavlink/blob/master/generator/mavgen.py#L44)). 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. The dialect definitions are: """ index_text_trailer=""" """ #Fix up the BeautifulSoup output so to fix build-link errors in the generated gitbook. ## BS puts each tag/content in its own line. Gitbook generates anchors using the spaces/newlines. ## This puts displayed text content immediately within tags so that anchors/links generate properly def fix_content_in_tags(input_html): #print("fix_content_in_tags was called") def remove_space_between_content_tags(matchobj): stripped_string=matchobj.group(1).strip() return '>%s<' % stripped_string input_html=re.sub(r'\>(\s+?\w+?.*?)\<', remove_space_between_content_tags, input_html,flags=re.DOTALL) return input_html def fix_external_dialect_link(input_html): #print("fix_external_dialect_link was called") def fixupexternaldialecturls(matchobj): return matchobj.group(1).strip() input_html=re.sub(r' **Tip** The common set `includes` [minimal.xml](minimal.md), which contains the *minimal set* of definitions for any MAVLink system. These definitions are [reproduced at the end of this topic](#minimal). """ elif filename == 'minimal': insert_text+=""" # MAVLink Minimal Set The MAVLink *minimal* set contains the minimal set of definitions for a viable MAVLink system. The message set is defined in [minimal.xml](https://github.com/mavlink/mavlink/blob/master/message_definitions/v1.0/minimal.xml) and is managed by the MAVLink project. > **Tip** The minimal set is included (imported into) other xml definition files, including the [MAVLink Common Message Set (common.xml)](minimal.md). """ elif filename == 'ardupilotmega': insert_text+=""" # Dialect: ArduPilotMega These messages define the ArduPilot specific message set, which is custom to [http://ardupilot.org](http://ardupilot.org). 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). > **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. """ elif filename == 'development': insert_text+=""" # Dialect: development This dialect contains messages that are proposed for inclusion in the [standard set](standard.md), in order to ease development of prototype implementations. They should be considered a 'work in progress' and not included in production builds. This topic is a human-readable form of the XML definition file: [development.xml](https://github.com/mavlink/mavlink/blob/master/message_definitions/v1.0/development.xml). """ elif filename == 'all': insert_text+=""" # Dialect: all This dialect is intended to `include` all other [dialects](../messages/README.md) in the mavlink/mavlink repository (including [external dialects](https://github.com/mavlink/mavlink/tree/master/external/dialects#mavlink-external-dialects)). Dialects that are in **all.xml** are guaranteed to not have clashes in messages, enums, enum ids, and MAV_CMDs. This ensure that: - Systems based on these dialects can co-exist on the same MAVLink network. - A Ground Station might (optionally) use libraries generated from **all.xml** to communicate using any of the dialects. > **Warning** New dialect files in the official repository must be added to **all.xml** and restrict themselves to using ids in their own allocated range. A few older dialects are not included because these operate in completely closed networks or because they are only used for tests. This topic is a human-readable form of the XML definition file: [all.xml](https://github.com/mavlink/mavlink/blob/master/message_definitions/v1.0/all.xml). """ else: insert_text+='\n# Dialect: %s' % filename.rsplit('.',1)[0] 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) insert_text+=""" > **Note** MAVLink 2 messages have an ID > 255 and are marked up using **(MAVLink 2)** in their description. > **Note** MAVLink 2 extension fields that have been added to MAVLink 1 messages are displayed in blue. """ # Include HTML in generated content insert_text+='\n\n{%% include "_html/%s.html" %%}' % filename input_html=insert_text+'\n\n'+input_html if filename == 'common': input_html+=""" # Minimal.xml {#minimal} The minimal set of definitions required for any MAVLink system are included from [minimal.xml](minimal.md). These are listed below. {% include "_html/minimal.html" %}""" #print(input_html) return input_html dialect_files = set() all_files = set() for subdir, dirs, files in os.walk(xml_message_definitions_dir_name): #Generate html for all the XML files for file in files: print(file) if not file.endswith('.xml'): #only process xml files. continue xml_file_name = xml_message_definitions_dir_name+file with open(xml_file_name, 'r') as content_file: xml_file = content_file.read() dom = ET.fromstring(xml_file) transform = ET.XSLT(xslt) newdom = transform(dom) #Prettify the HTML using BeautifulSoup soup=bs(str(newdom), "lxml") prettyHTML=soup.prettify() #Strip out text before tag in XSLT output prettyHTML=strip_text_before_string(prettyHTML,'') prettyHTML = fix_content_in_tags(prettyHTML) #Replace invalid file extensions (workaround for xslt) prettyHTML = fix_include_file_extension(prettyHTML) #Replace space markers with intentional space prettyHTML = fix_replace_space_marker(prettyHTML) #Fix up links to external dialects to not be links prettyHTML = fix_external_dialect_link(prettyHTML) #Fix up plain text mav symbols to be internal links prettyHTML = fix_add_implicit_links_items(prettyHTML) #Write output html file output_file_name_html = file.rsplit('.',1)[0]+".html" output_file_name_html_withdir = output_dir_html+output_file_name_html print("Output filename (html): %s" % output_file_name_html) with open(output_file_name_html_withdir, 'w') as out: out.write(prettyHTML) # Create sortable list of output file names #Write output markdown file output_file_name_prefix = file.rsplit('.',1)[0] all_files.add(output_file_name_prefix) if not file=='common.xml' and not file=='standard.xml' and not file=='minimal.xml' and not file=='test.xml' and not file=='development.xml': dialect_files.add(output_file_name_prefix) # Generate the markdown files for file_prefix in all_files: print(file_prefix) markdown_text='' #Inject a heading and doc-type intro (markdown format) markdown_text = inject_top_level_docs(markdown_text,file_prefix) output_file_name_md_withdir = output_dir+file_prefix+'.md' print("Output filename (md): %s" % output_file_name_md_withdir) with open(output_file_name_md_withdir, 'w') as out: out.write(markdown_text) for the_file in sorted(dialect_files): index_text+='\n* [%s.xml](%s.md)' % (the_file,the_file) index_text+='\n\n' index_text+=index_text_trailer #Write the index with open(index_file_name, 'w') as content_file: content_file.write(index_text) print("COMPLETED")