#!/usr/bin/env python3 """ Extract fields from .msg for failsafe state machine simulation """ import math import os import argparse import sys import re if __name__ == "__main__": parser = argparse.ArgumentParser(description='Extract fields from .msg files') parser.add_argument('msg_file', help='FailsafeFlags.msg file') parser.add_argument('header_file', help='generated_uorb_struct_field_mapping.h') parser.add_argument('html_template', help='HTML template input file') parser.add_argument('html_output', help='HTML output file') args = parser.parse_args() msg_filename = args.msg_file header_file = args.header_file html_template_file = args.html_template html_output_file = args.html_output msg_name = os.path.splitext(os.path.basename(msg_filename))[0] groups = [] class Group: def __init__(self, group_name): self.name = group_name self.fields = [] # tuples of (field_name, comment) self.new_row = False group_name = '' new_group = True num_fields = 0 with open(msg_filename, 'r') as lineparser: while True: line = lineparser.readline() if not line: break line = line.strip() if line == '': new_group = True continue if line.startswith('#'): # use comment & empty lines for grouping and group names group_name = line[1:].strip() continue if not line.startswith('bool') and not line.startswith('uint8'): # only booleans & uint8 supported continue field_search = re.search('^(\w+)\s+(\w+)', line) field_name = field_search.group(2) if field_name.startswith('mode_req_') or field_name == 'timestamp': continue field_search = re.search('^(\w+)\s+(\w+)\s+#(.*)$', line) if not field_search: raise Exception("{:}:\nFailed to extract fields from '{:}' (missing comment?)".format(msg_filename, line)) if new_group: groups.append(Group(group_name)) new_group = False group_name = '' groups[-1].fields.append((field_search.group(1), field_search.group(2), field_search.group(3).strip())) num_fields += 1 # Split conditions into rows num_rows = 2 num_fields_per_row = math.ceil(num_fields / num_rows) current_num_fields = 0 for group in groups: current_num_fields += len(group.fields) if current_num_fields > num_fields_per_row: group.new_row = True current_num_fields = 0 # Header file macro_lines = '' for group in groups: for field_type, field_name, comment in group.fields: macro_lines += ' .property("{0}", &px4::msg::{1}::{0}) \\\n'.format(field_name, msg_name) cpp_emscription_macro = '#define UORB_STRUCT_FIELD_MAPPING \\\n{}\n'.format(macro_lines) with open(header_file, 'w') as file: file.write(cpp_emscription_macro) # JS + Html js_tag = '{{{ JS_STATE_CODE }}}' html_tag = '{{{ HTML_CONDITIONS }}}' js_code = '' html_conditions = '' html_conditions += '
' for group in groups: if group.new_row: html_conditions += '' html_conditions += '

' if group.name != '': html_conditions += '

' + group.name + '
' for field_type, field_name, comment in group.fields: if field_type == 'bool': js_code += " state.{0} = document.querySelector('input[id=\"{0}\"]').checked;\n".format(field_name) html_conditions += '''
'''.format(field_name, comment) elif field_type == 'uint8': assert field_name == 'battery_warning', "only battery_warning supported for uint8 type" js_code += " state.{0} = document.querySelector('select[id=\"{0}\"]').value;\n".format(field_name) html_conditions += '''
'''.format(field_name, comment) else: raise Exception("Unsupported type: {:}".format(field_type)) html_conditions += '

' html_conditions += '
' with open(html_template_file, 'r') as file: html_template_content = file.read() assert js_tag in html_template_content, "js_tag not found in {}".format(html_template_file) assert html_tag in html_template_content, "html_tag not found in {}".format(html_template_file) html_template_content = html_template_content.replace(js_tag, js_code) html_template_content = html_template_content.replace(html_tag, html_conditions) with open(html_output_file, 'w') as file: file.write(html_template_content)