# Copyright 2024 Volvo Car Corporation # Licensed under Apache 2.0. """Module containing classes for generation of signal consistency check report.""" import json from powertrain_build.signal_incons_html_rep_base import SigConsHtmlReportBase class SigConsHtmlReportAll(SigConsHtmlReportBase): """Generate html report from the signal consistency check result. (see :doc:`signal_interfaces`) Inherits :doc:`HtmlReport `. """ __intro_all = """

Introduction

This documents lists inconsistencies in the internal and external signal configuration.

""" __table_incons = """ """ __toc_ext_sig = """

Table of contents

'
  1. External signals
    1. Missing external signals
    2. Unused external signals
  2. Signals missing and unused in the interface definition

    \n """ _toc_unit_details = """

    Table of contents

    1. Detailed unit information
    2. Unit index
    """ def __init__(self, res_dicts=None): """Initialize class instance. Args: res_dict (dict): result dict from the signal interface consistency check The dict shall have the following format:: { "sigs": { "ext": {"missing": {}, "unused": {}, "inconsistent_defs": {}}, "int": {"UNIT_NAME": {"missing": {}, "unused": {}, "multiple_defs": {} "inconsistent_defs": {}} }, "never_active_signals": { "UNIT_NAME": [signal_one, ...] } } """ super().__init__(res_dicts) self.set_res_dict(res_dicts) def _gen_unit_toc(self): """Generate a unit TOC for the unit with signal inconsistencies. Hyperlinks to more in depth unit information. """ self._out_all_unit_toc = {} for prj, units in self._all_units.items(): out = '

    Unit index

    \n' for unit in sorted(units): out += f'
    {unit}
    \n' self._out_all_unit_toc.update({prj: out}) def _gen_units(self): """Generate all the information regarding all the units.""" self._out_all_units = {} self._prj = '' for prj, units in self._all_units.items(): out = '

    Detailed Unit Information

    \n' for unit in sorted(units): out += self._gen_unit(prj, unit) self._out_all_units.update({prj: out}) return self._out_all_units def _gen_unit(self, project, unit): """Generate the html-report for the unit specific information.""" unit_data_all = {} out = f'

    {unit}

    \n' if project in self._int_units_all and unit in self._int_units_all[project]: for prj, res_dict in self._res_dict_all.items(): if unit in res_dict['sigs']['int']: unit_data = res_dict['sigs']['int'][unit] unit_data_all.update({prj: unit_data}) _res_dict = self._res_dict_all.get(project) unit_data = _res_dict['sigs']['int'][unit] out += '

    Missing signals

    \n' \ '

    Inports whose signals are not generated in the ' \ 'listed configuration(s).

    ' out += self._gen_missing_sigs(unit_data, unit_data_all) out += '

    Unused signals

    \n' \ '

    Outports that are generated, but not used in the ' \ 'listed configuration(s).

    ' out += self._gen_unused_sigs(unit_data, unit_data_all) out += '

    Multiple defined signals

    \n' \ '

    Outports that are generated more than once in ' \ 'the listed configuration(s).

    ' out += self._gen_multiple_def_sigs(unit_data, unit_data_all) out += '

    Internal signal inconsistencies

    \n' \ '

    Inports that have different variable definitions ' \ 'than the producing outport.

    ' out += self._gen_int_inconsistent_defs(unit_data, unit_data_all) if project in self._ext_units_all and unit in self._ext_units_all[project]: out += self._gen_unit_ext(project, unit) if project in self._res_dict_all and \ 'never_active_signals' in self._res_dict_all[project] and \ unit in self._res_dict_all[project]['never_active_signals']: out += '

    Never active signals

    \n' \ '

    Never active signals will not appear in generated .c file, ' \ 'signals probablty lead to terminators in Simulink model.

    ' out += self._gen_never_active_sigs(self._res_dict_all[project]['never_active_signals'][unit]) return out def _gen_unit_ext(self, project, unit): out = '' self._unit_data_all = {} if unit in self._res_dict_all[project]['sigs']['ext']['inconsistent_defs'].keys(): out += '

    External signal inconsistencies

    \n' \ '

    In-/Out-ports that have different variable definitions ' \ 'than in the interface definition file.

    ' out += self._gen_ext_inconsistent_defs(project, unit) return out def _gen_signals_table(self, unit_data, unit_data_all, key, out=''): """Generate the unit specific information for KEY in a unit.""" if key not in unit_data: return out out += self._table_unused for var in sorted(unit_data[key]): configs_str = "" for unit_data_cfg in unit_data_all.values(): if key in unit_data_cfg and var in unit_data_cfg[key]: configs = unit_data_cfg[key][var] configs_str += f" {self._set_to_str(configs)}" out += '
\n' out += f' \n' out += f' \n' out += ' \n' out += ' \n' out += '
Variable Variable parameter Difference Configurations
{var}{configs_str}
\n' return out def _gen_missing_sigs(self, unit_data, unit_data_all, out=''): """Generate the unit specific information for missing signal in a unit.""" return self._gen_signals_table(unit_data, unit_data_all, key='missing', out=out) def _gen_unused_sigs(self, unit_data, unit_data_all, out=''): """Generate the unit specific information for the unused signals wihtin a unit.""" return self._gen_signals_table(unit_data, unit_data_all, key='unused', out=out) def _gen_multiple_def_sigs(self, unit_data, unit_data_all, out=''): """Generate unit specific information for the signals that are generated more than once.""" return self._gen_signals_table(unit_data, unit_data_all, key='multiple_defs', out=out) def _gen_never_active_sigs(self, never_active_signals): """Generate unit specific information for the signals that are never active.""" out = self._table_unused for signal in never_active_signals: out += ' \n' out += f' {signal}\n' out += ' \n' out += ' \n' out += ' \n' out += ' \n' return out def _gen_ext_inconsistent_defs(self, project, unit, out=''): """Generate a report of inconsistent variable definition parameters. Report inconsistencies between the producing signal definition, and the signal definitions in the external interface definition. """ inconsistent_defs_key = 'inconsistent_defs' if inconsistent_defs_key not in self._res_dict_all[project]['sigs']['ext']: return out out += self.__table_incons incons_unit = self._res_dict_all[project]['sigs']['ext'][inconsistent_defs_key][unit] for var in sorted(incons_unit.keys()): first_cells = f'\n \n {var}\n' for v_par, desc in incons_unit[var].items(): out += first_cells out += f' {v_par}\n' out += f' {desc}\n' out += f' {project}\n' out += ' \n' first_cells = ' \n \n' out += ' \n' out += ' \n' return out def _gen_int_inconsistent_defs(self, unit_data, unit_data_all, out=''): """Generate a report of inconsistent variable definition parameters. Inconsistent for between the producing signal definition, and the consuming signal definitions for SPM internal signals. """ inconsistent_defs_key = 'inconsistent_defs' if inconsistent_defs_key not in unit_data: return out out += self.__table_incons for var in sorted(unit_data[inconsistent_defs_key].keys()): configs_str = "" for prj_cfg in unit_data_all.keys(): configs_str += f" {prj_cfg}" first_cells = f'\n \n {var}\n' configs = unit_data[inconsistent_defs_key][var] for v_par, desc in configs.items(): out += first_cells out += f' {v_par}\n' out += f' {desc}\n' out += f' {configs_str}\n' out += ' \n' first_cells = ' \n \n' out += ' \n' out += ' \n' return out def _gen_ext_signals_report(self, type_, comment): """Generate report for external signals.""" out = f'

{type_.capitalize()} external signals

\n' out += f'

{comment}

' try: res_dict = self._res_dict_all.get(self._prj) out += self._table_unused ext_data = res_dict['sigs']['ext'][type_] for var in sorted(ext_data.keys()): configs_str = "" for res_dict_cfg in self._res_dict_all.values(): if type_ in res_dict_cfg['sigs']['ext']: ext_data_cfg = res_dict_cfg['sigs']['ext'][type_] configs = ext_data_cfg[var] configs_str += " " + self._set_to_str(configs) out += ' \n' out += f' {var}\n' out += f' {configs_str}\n' out += ' \n' out += ' \n' out += ' \n' except KeyError: pass return out def set_res_dict(self, res_dicts): """Set the result dictionary used to generate the html-report. Args: res_dicts (dict): result dict from the signal interface consistency check See class constructor for dict structure. """ # nesting defaultdicts is bad so we use this hack regular_res_dicts = json.loads(json.dumps(res_dicts)) self._res_dict_all = regular_res_dicts self._ext_units_all = {} self._int_units_all = {} self._all_units = {} for prj, res_dict in self._res_dict_all.items(): _ext_units = set() _int_units = set() _units_with_never_active_signals = set() if res_dict is not None and 'sigs' in res_dict: if 'ext' in res_dict['sigs'] and 'inconsistent_defs' in res_dict['sigs']['ext']: _ext_units = set(res_dict['sigs']['ext']['inconsistent_defs'].keys()) self._ext_units_all.update({prj: _ext_units}) if 'int' in res_dict['sigs']: _int_units = set(res_dict['sigs']['int'].keys()) self._int_units_all.update({prj: _int_units}) if 'never_active_signals' in res_dict: _units_with_never_active_signals = res_dict['never_active_signals'].keys() self._all_units[prj] = _ext_units | _int_units | _units_with_never_active_signals def _gen_contents_intro(self): """Generate report contents from the signal interfaces data.""" html = [] html += self.__intro_all return ''.join(html) def _gen_contents_toc_ext_sig(self): """Generate report contents from the signal interfaces data. Specialises HtmlReport.gen_contents() """ html = [] html += self.__toc_ext_sig return ''.join(html) def _gen_contents_toc_unit_details(self): """Generate report contents from the signal interfaces data. Specialises HtmlReport.gen_contents() """ html = [] html += self._toc_unit_details return ''.join(html) def _gen_contents_ext_signals(self): """Generate report contents from the signal inconsistency check result dictionary.""" html = [] html += self._gen_contents_toc_ext_sig() html += self._gen_ext_signals_report('missing', 'Signals not generated by ' 'Vcc SW, but are defined in ' 'the Interface definition to be ' 'generated') html += self._gen_ext_signals_report('unused', 'Signals defined to be generated by ' 'supplier SW, but are not used ' 'by VCC SW') return ''.join(html) def _generate_report_string_intro(self): """Generate a html report as string.""" html = [] html += self._gen_header() html += self._gen_contents_intro() html += self._gen_end() return ''.join(html) def _generate_report_string_ext_signals(self): """Generate a html report as string.""" html = [] html += self._gen_header() html += self._gen_contents_ext_signals() html += self._gen_end() return ''.join(html) def generate_report_file_signal_check(self, filename): """Generate a html report and save to file.""" filename_intro = filename + '_intro.html' with open(filename_intro, 'w', encoding="utf-8") as fhndl: fhndl.write(self._generate_report_string_intro()) self._gen_units() self._gen_unit_toc() for key, out in self._out_all_units.items(): html = [] html += self._gen_header() html += self._gen_contents_toc_unit_details() html += out html += self._out_all_unit_toc.get(key, '') html += self._gen_end() with open(f'{filename}_{key}.html', 'w', encoding="utf-8") as fhndl: fhndl.write(''.join(html))