From 91ce7626bcdc519eff90731fa723a7ccbb029d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Fri, 5 Dec 2025 18:12:41 +0100 Subject: [PATCH 01/28] Added Template Instantiator --- Makefile | 2 +- templateprocessor/templateinstantiator.py | 36 ++++ tests/Makefile | 3 +- tests/test_templateinstantiator.py | 234 ++++++++++++++++++++++ 4 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 templateprocessor/templateinstantiator.py create mode 100644 tests/test_templateinstantiator.py diff --git a/Makefile b/Makefile index 640171b..b3cf4ce 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ PYTHON ?= python3 all: check-format check install: - pipx install . + pipx install --force . check: $(MAKE) -C tests check diff --git a/templateprocessor/templateinstantiator.py b/templateprocessor/templateinstantiator.py new file mode 100644 index 0000000..e8e02d1 --- /dev/null +++ b/templateprocessor/templateinstantiator.py @@ -0,0 +1,36 @@ +""" +Template Instantiator. + +This module is responsible for instantiating Mako templates using the provided data. +""" + +from templateprocessor.iv import InterfaceView +from templateprocessor.so import SystemObjectType, SystemObject +from typing import List +from mako.template import Template + + +class TemplateInstantiator: + """ + Instantiator of Mako templates + """ + + system_object_types: List[SystemObjectType] = list() + interface_view: InterfaceView + + def __init__( + self, interface_view: InterfaceView, system_object_types: List[SystemObjectType] + ): + self.system_object_types = system_object_types + self.interface_view = interface_view + + def instantiate(self, template: str, context_directory: str) -> str: + mako_template = Template(text=template, module_directory=context_directory) + + context = { + "system_object_types": self.system_object_types, + "interface_view": self.interface_view, + } + + instantiated_text = str(mako_template.render(**context)) + return instantiated_text diff --git a/tests/Makefile b/tests/Makefile index f599055..f405cbc 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,7 +3,8 @@ PYTHON ?= python3 TESTS = \ test_ivreader.py \ - test_soreader.py + test_soreader.py \ + test_templateinstantiator.py .PHONY: \ check diff --git a/tests/test_templateinstantiator.py b/tests/test_templateinstantiator.py new file mode 100644 index 0000000..648028f --- /dev/null +++ b/tests/test_templateinstantiator.py @@ -0,0 +1,234 @@ +""" +Tests for TemplateInstantiator class +""" + +import pytest +import tempfile +from pathlib import Path +from typing import List +from templateprocessor.templateinstantiator import TemplateInstantiator +from templateprocessor.iv import ( + InterfaceView, + Function, + Language, + ProvidedInterface, + RequiredInterface, + InterfaceKind, + InputParameter, + OutputParameter, + Encoding, +) +from templateprocessor.so import SystemObjectType, SystemObject + + +class TestTemplateInstantiator: + """Test cases for TemplateInstantiator class.""" + + @staticmethod + def create_sample_interface_view() -> InterfaceView: + """Create a sample InterfaceView for testing.""" + iv = InterfaceView( + version="1.3", + asn1file="test.acn", + uiFile="test.ui.xml", + modifierHash="test_hash", + ) + + # Create a sample function + func = Function( + id="func_1", name="TestFunction", is_type=False, language=Language.C + ) + + # Add a provided interface + pi = ProvidedInterface( + id="pi_1", + name="test_pi", + kind=InterfaceKind.SPORADIC, + ) + pi.input_parameters = [ + InputParameter(name="input1", type="MyInt", encoding=Encoding.NATIVE) + ] + func.provided_interfaces = [pi] + + # Add a required interface + ri = RequiredInterface( + id="ri_1", + name="test_ri", + kind=InterfaceKind.CYCLIC, + ) + ri.output_parameters = [ + OutputParameter(name="output1", type="MyFloat", encoding=Encoding.UPER) + ] + func.required_interfaces = [ri] + + iv.functions = [func] + return iv + + @staticmethod + def create_sample_system_object_types() -> List[SystemObjectType]: + """Create sample SystemObjectTypes for testing.""" + # Create events system object type + events = SystemObjectType() + events.property_names = ["ID", "Name", "Severity"] + + event1 = SystemObject() + event1.values = {"ID": "1", "Name": "Error Event", "Severity": "high"} + event2 = SystemObject() + event2.values = {"ID": "2", "Name": "Info Event", "Severity": "low"} + + events.instances = [event1, event2] + + # Create parameters system object type + params = SystemObjectType() + params.property_names = ["ID", "Name", "Default"] + + param1 = SystemObject() + param1.values = {"ID": "1", "Name": "Timeout", "Default": "100"} + param2 = SystemObject() + param2.values = {"ID": "2", "Name": "MaxRetries", "Default": "3"} + + params.instances = [param1, param2] + + return [events, params] + + def test_instantiator_initialization(self): + """Test TemplateInstantiator initialization.""" + iv = self.create_sample_interface_view() + so_types = self.create_sample_system_object_types() + + instantiator = TemplateInstantiator(iv, so_types) + + assert instantiator.interface_view == iv + assert instantiator.system_object_types == so_types + assert len(instantiator.system_object_types) == 2 + + def test_instantiate_simple_template(self): + """Test instantiating a simple template.""" + iv = self.create_sample_interface_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, so_types) + + template = "Hello World!" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert result == "Hello World!" + + def test_instantiate_template_with_interface_view(self): + """Test instantiating a template that uses Interface View.""" + iv = self.create_sample_interface_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, so_types) + + template = """Interface View version: ${interface_view.version} +ASN1 file: ${interface_view.asn1file} +Number of functions: ${len(interface_view.functions)}""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Interface View version: 1.3" in result + assert "ASN1 file: test.acn" in result + assert "Number of functions: 1" in result + + def test_instantiate_template_with_function_details(self): + """Test instantiating a template that accesses Function details.""" + iv = self.create_sample_interface_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, so_types) + + template = """% for func in interface_view.functions: +Function: ${func.name} +Language: ${func.language.value} +Provided Interfaces: ${len(func.provided_interfaces)} +Required Interfaces: ${len(func.required_interfaces)} +% endfor""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Function: TestFunction" in result + assert "Language: C" in result + assert "Provided Interfaces: 1" in result + assert "Required Interfaces: 1" in result + + def test_instantiate_template_with_system_object_types(self): + """Test instantiating a template that uses System Object Types.""" + iv = self.create_sample_interface_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, so_types) + + template = """Number of System Object Types: ${len(system_object_types)} +% for so_type in system_object_types: + Properties: ${', '.join(so_type.property_names)} + Instances: ${len(so_type.instances)} +% endfor""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Number of System Object Types: 2" in result + assert "Properties: ID, Name, Severity" in result + assert "Properties: ID, Name, Default" in result + assert "Instances: 2" in result + + def test_instantiate_template_with_system_object_instances(self): + """Test instantiating a template that accesses System Object Type instances.""" + iv = self.create_sample_interface_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, so_types) + + template = """% for so_type in system_object_types: +% for instance in so_type.instances: +% if 'Name' in instance.values: + - ID: ${instance.values['ID']} - ${instance.values['Name']}} +% endif +% endfor +% endfor""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert " ID: 1 - Error Event" in result + assert " ID: 2 - Info Event" in result + assert " ID: 1 - Timeout" in result + assert " ID: 2 - MaxRetries" in result + + def test_instantiate_template_with_empty_data(self): + """Test instantiating a template with empty Interface View and no System Objects.""" + iv = InterfaceView(version="1.0", asn1file="", uiFile="", modifierHash="") + + so_types = [] + + instantiator = TemplateInstantiator(iv, so_types) + + template = """Version: ${interface_view.version} +Functions: ${len(interface_view.functions)} +System Object Types: ${len(system_object_types)}""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Version: 1.0" in result + assert "Functions: 0" in result + assert "System Object Types: 0" in result + + def test_instantiate_template_with_python_expressions(self): + """Test instantiating a template with Python expressions.""" + iv = self.create_sample_interface_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, so_types) + + template = """<% +total_interfaces = sum(len(f.provided_interfaces) + len(f.required_interfaces) for f in interface_view.functions) +total_instances = sum(len(so.instances) for so in system_object_types) +%> +Total Interfaces: ${total_interfaces} +Total System Object Instances: ${total_instances}""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Total Interfaces: 2" in result + assert "Total System Object Instances: 4" in result From 88edd66fd51e78a899855813aa856e7876b86566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Fri, 5 Dec 2025 18:32:46 +0100 Subject: [PATCH 02/28] Added names to SystemObjectTypes --- templateprocessor/templateinstantiator.py | 8 +++-- tests/test_templateinstantiator.py | 44 ++++++++++++++--------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/templateprocessor/templateinstantiator.py b/templateprocessor/templateinstantiator.py index e8e02d1..8883eb9 100644 --- a/templateprocessor/templateinstantiator.py +++ b/templateprocessor/templateinstantiator.py @@ -6,7 +6,7 @@ from templateprocessor.iv import InterfaceView from templateprocessor.so import SystemObjectType, SystemObject -from typing import List +from typing import List, Dict from mako.template import Template @@ -15,11 +15,13 @@ class TemplateInstantiator: Instantiator of Mako templates """ - system_object_types: List[SystemObjectType] = list() + system_object_types: Dict[str, SystemObjectType] = dict() interface_view: InterfaceView def __init__( - self, interface_view: InterfaceView, system_object_types: List[SystemObjectType] + self, + interface_view: InterfaceView, + system_object_types: Dict[str, SystemObjectType], ): self.system_object_types = system_object_types self.interface_view = interface_view diff --git a/tests/test_templateinstantiator.py b/tests/test_templateinstantiator.py index 648028f..d25c572 100644 --- a/tests/test_templateinstantiator.py +++ b/tests/test_templateinstantiator.py @@ -5,7 +5,7 @@ import pytest import tempfile from pathlib import Path -from typing import List +from typing import Dict, List from templateprocessor.templateinstantiator import TemplateInstantiator from templateprocessor.iv import ( InterfaceView, @@ -65,7 +65,7 @@ def create_sample_interface_view() -> InterfaceView: return iv @staticmethod - def create_sample_system_object_types() -> List[SystemObjectType]: + def create_sample_system_object_types() -> Dict[str, SystemObjectType]: """Create sample SystemObjectTypes for testing.""" # Create events system object type events = SystemObjectType() @@ -89,7 +89,11 @@ def create_sample_system_object_types() -> List[SystemObjectType]: params.instances = [param1, param2] - return [events, params] + sots = dict() + sots["events"] = events + sots["params"] = params + + return sots def test_instantiator_initialization(self): """Test TemplateInstantiator initialization.""" @@ -160,17 +164,17 @@ def test_instantiate_template_with_system_object_types(self): instantiator = TemplateInstantiator(iv, so_types) template = """Number of System Object Types: ${len(system_object_types)} -% for so_type in system_object_types: - Properties: ${', '.join(so_type.property_names)} - Instances: ${len(so_type.instances)} +% for name, so_type in system_object_types.items(): + [${name}] Properties: ${', '.join(so_type.property_names)} + [${name}] Instances: ${len(so_type.instances)} % endfor""" with tempfile.TemporaryDirectory() as tmpdir: result = instantiator.instantiate(template, tmpdir) assert "Number of System Object Types: 2" in result - assert "Properties: ID, Name, Severity" in result - assert "Properties: ID, Name, Default" in result + assert "[events] Properties: ID, Name, Severity" in result + assert "[params] Properties: ID, Name, Default" in result assert "Instances: 2" in result def test_instantiate_template_with_system_object_instances(self): @@ -179,27 +183,33 @@ def test_instantiate_template_with_system_object_instances(self): so_types = self.create_sample_system_object_types() instantiator = TemplateInstantiator(iv, so_types) - template = """% for so_type in system_object_types: + template = """% for name, so_type in system_object_types.items(): +[${name}] % for instance in so_type.instances: % if 'Name' in instance.values: - - ID: ${instance.values['ID']} - ${instance.values['Name']}} + - ID: ${instance.values['ID']} - ${instance.values['Name']} % endif % endfor % endfor""" with tempfile.TemporaryDirectory() as tmpdir: result = instantiator.instantiate(template, tmpdir) - - assert " ID: 1 - Error Event" in result - assert " ID: 2 - Info Event" in result - assert " ID: 1 - Timeout" in result - assert " ID: 2 - MaxRetries" in result + assert ( + """[events] + - ID: 1 - Error Event + - ID: 2 - Info Event +[params] + - ID: 1 - Timeout + - ID: 2 - MaxRetries +""" + == result + ) def test_instantiate_template_with_empty_data(self): """Test instantiating a template with empty Interface View and no System Objects.""" iv = InterfaceView(version="1.0", asn1file="", uiFile="", modifierHash="") - so_types = [] + so_types = {} instantiator = TemplateInstantiator(iv, so_types) @@ -222,7 +232,7 @@ def test_instantiate_template_with_python_expressions(self): template = """<% total_interfaces = sum(len(f.provided_interfaces) + len(f.required_interfaces) for f in interface_view.functions) -total_instances = sum(len(so.instances) for so in system_object_types) +total_instances = sum(len(so.instances) for so in system_object_types.values()) %> Total Interfaces: ${total_interfaces} Total System Object Instances: ${total_instances}""" From 9e741d9b857b4255da6553bef8d66b2ea7a59595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Fri, 5 Dec 2025 19:40:12 +0100 Subject: [PATCH 03/28] Working proof of concept --- examples/generate_so_list.sh | 3 + examples/so_list.tmplt | 8 +++ setup.py | 2 +- templateprocessor/cli.py | 104 +++++++++++++++++++++++++++++++---- templateprocessor/iv.py | 8 +-- 5 files changed, 110 insertions(+), 15 deletions(-) create mode 100755 examples/generate_so_list.sh create mode 100644 examples/so_list.tmplt diff --git a/examples/generate_so_list.sh b/examples/generate_so_list.sh new file mode 100755 index 0000000..298f8bb --- /dev/null +++ b/examples/generate_so_list.sh @@ -0,0 +1,3 @@ +#!/bin/bash +mkdir -p output +template-processor --verbosity info --sos ../data/events.csv -o output -t so_list.tmplt \ No newline at end of file diff --git a/examples/so_list.tmplt b/examples/so_list.tmplt new file mode 100644 index 0000000..2260540 --- /dev/null +++ b/examples/so_list.tmplt @@ -0,0 +1,8 @@ +### List of all System Objects +% for name, so_type in system_object_types.items(): +[${name}] +Properties: ${', '.join(so_type.property_names)} +% for idx, instance in enumerate(so_type.instances): +Instance ${idx}: ${', '.join(instance.values.values())} +% endfor +% endfor \ No newline at end of file diff --git a/setup.py b/setup.py index ec90987..55d60ff 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ include_package_data=True, python_requires='>=3.8', install_requires=[ - # Add project dependencies here + "mako==1.3.10" ], extras_require={ 'dev': [ diff --git a/templateprocessor/cli.py b/templateprocessor/cli.py index 6884b35..0c43c53 100644 --- a/templateprocessor/cli.py +++ b/templateprocessor/cli.py @@ -2,13 +2,19 @@ Command Line Interface for Template Processor """ +import logging import argparse +import os +from pathlib import Path import sys from templateprocessor import __version__ +from templateprocessor.iv import InterfaceView +from templateprocessor.templateinstantiator import TemplateInstantiator +from templateprocessor.ivreader import IVReader +from templateprocessor.soreader import SOReader -def main(): - """Main entry point for the Template Processor CLI.""" +def parse_arguments() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Template Processor - Template processing engine for TASTE Document Generator", formatter_class=argparse.RawDescriptionHelpFormatter, @@ -19,24 +25,102 @@ def main(): ) parser.add_argument( - "-i", - "--input", - help="Input data file (e.g., TASTE Interface View data)", + "-i", "--iv", help="Input Interface View", metavar="FILE", + ) + + parser.add_argument( + "-s", + "--sos", + help="Input System Objects provided as CSV files (each as a separate argument)", + metavar="FILE", + action="append", + ) + + parser.add_argument( + "-t", + "--template", + help="Template file to process (each as a separate argument)", metavar="FILE", + action="append", + ) + + parser.add_argument( + "-o", + "--output", + help="Output directory for processed templates", + metavar="DIR", + required=True, ) parser.add_argument( - "-t", "--template", help="Template file to process", metavar="FILE" + "--verbosity", + choices=["info", "debug", "warning", "error"], + default="warning", + help="Logging verbosity", ) parser.add_argument( - "-o", "--output", help="Output file for processed template", metavar="FILE" + "-p", + "--postprocess", + choices=["none", "md2docx"], + help="Output postprocessing", + default="none", ) - args = parser.parse_args() + return parser.parse_args() + + +def get_log_level(level_str: str) -> int: + log_levels = { + "info": logging.INFO, + "debug": logging.DEBUG, + "warning": logging.WARNING, + "error": logging.ERROR, + } + + return log_levels.get(level_str.lower(), logging.WARNING) + + +def main(): + """Main entry point for the Template Processor CLI.""" + + args = parse_arguments() + logging_level = get_log_level(args.verbosity) + logging.basicConfig(level=logging_level) + + logging.info("Template Processor") + logging.debug(f"Interface View: {args.iv}") + logging.debug(f"System Objects: {args.sos}") + logging.debug(f"Templates: {args.template}") + logging.debug(f"Output Directory: {args.output}") + + logging.info(f"Reading Interface View from {args.iv}") + iv = IVReader().read(args.iv) if args.iv else InterfaceView() + sots = {} + so_reader = SOReader() + for sot_file in args.sos: + logging.info(f"Reading System Objects from {sot_file}") + name = Path(sot_file).stem + logging.debug(f"-SOT name: {name}") + sos = so_reader.read(sot_file) + sots[name] = sos + + instantiator = TemplateInstantiator(iv, sots) - print("Template Processor - Not yet implemented") - print(f"Version: {__version__}") + for template_file in args.template: + logging.info(f"Processing template {template_file}") + name = Path(template_file).stem + logging.debug(f"Base name: {name}") + logging.debug(f"Reading template {template_file}") + with open(template_file, "r") as f: + template = f.read() + logging.debug(f"Instantiating template:\n {template}") + instantiated_template = instantiator.instantiate(template, "") + logging.debug(f"Instantiation:\n {instantiated_template}") + output = Path(args.output) / f"{name}.md" + logging.debug(f"Saving to {output}") + with open(output, "w") as f: + f.write(instantiated_template) return 0 diff --git a/templateprocessor/iv.py b/templateprocessor/iv.py index 734e9a1..6b486d4 100644 --- a/templateprocessor/iv.py +++ b/templateprocessor/iv.py @@ -200,10 +200,10 @@ class InterfaceView: and other elements that define a TASTE system's interface architecture. """ - version: str - asn1file: str - uiFile: str - modifierHash: str + version: str = "" + asn1file: str = "" + uiFile: str = "" + modifierHash: str = "" functions: List[Function] = field(default_factory=list) connections: List[Connection] = field(default_factory=list) comments: List[Comment] = field(default_factory=list) From 9e663c076d312fa47114b3ae109d5a236f947274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Mon, 8 Dec 2025 13:51:20 +0100 Subject: [PATCH 04/28] Added parsing of requirement IDs --- data/requirements.iv.xml | 37 +++++++++++++++++++++++++++++++++++ templateprocessor/cli.py | 5 ++++- templateprocessor/iv.py | 2 ++ templateprocessor/ivreader.py | 2 ++ tests/test_ivreader.py | 30 ++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 data/requirements.iv.xml diff --git a/data/requirements.iv.xml b/data/requirements.iv.xml new file mode 100644 index 0000000..43f932e --- /dev/null +++ b/data/requirements.iv.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templateprocessor/cli.py b/templateprocessor/cli.py index 0c43c53..ea945e9 100644 --- a/templateprocessor/cli.py +++ b/templateprocessor/cli.py @@ -25,7 +25,10 @@ def parse_arguments() -> argparse.Namespace: ) parser.add_argument( - "-i", "--iv", help="Input Interface View", metavar="FILE", + "-i", + "--iv", + help="Input Interface View", + metavar="FILE", ) parser.add_argument( diff --git a/templateprocessor/iv.py b/templateprocessor/iv.py index 6b486d4..e67865f 100644 --- a/templateprocessor/iv.py +++ b/templateprocessor/iv.py @@ -92,6 +92,7 @@ class FunctionInterface: input_parameters: List[InputParameter] = field(default_factory=list) output_parameters: List[OutputParameter] = field(default_factory=list) properties: List[Property] = field(default_factory=list) + requirement_ids: List[str] = field(default_factory=list) @dataclass @@ -138,6 +139,7 @@ class Function: properties: List[Property] = field(default_factory=list) nested_functions: List["Function"] = field(default_factory=list) nested_connections: List["Connection"] = field(default_factory=list) + requirement_ids: List[str] = field(default_factory=list) @dataclass diff --git a/templateprocessor/ivreader.py b/templateprocessor/ivreader.py index 493cda7..3557eec 100644 --- a/templateprocessor/ivreader.py +++ b/templateprocessor/ivreader.py @@ -134,6 +134,7 @@ def _parse_function(self, elem: ET.Element) -> Function: if elem.get("type_language") else None ), + requirement_ids=elem.get("requirement_ids", "").split(","), ) # Parse properties @@ -192,6 +193,7 @@ def _parse_interface(self, elem: ET.Element) -> FunctionInterface: else None ), priority=int(elem.get("priority")) if elem.get("priority") else None, + requirement_ids=elem.get("requirement_ids", "").split(","), ) # Parse input parameters diff --git a/tests/test_ivreader.py b/tests/test_ivreader.py index 4377a16..dd33d4d 100644 --- a/tests/test_ivreader.py +++ b/tests/test_ivreader.py @@ -249,3 +249,33 @@ def test_read_string(self): assert len(iv.functions) == 1 assert iv.functions[0].name == "test_func" assert len(iv.layers) == 1 + + def test_read_requirements(self): + """Test parsing interface with requirement IDs.""" + # Prepare + reader = IVReader() + iv_file = self.get_test_data_file("requirements.iv.xml") + assert iv_file.exists() + + # Read + iv = reader.read(iv_file) + + # Find Function_1 function + function1 = next((f for f in iv.functions if f.name == "Function_1"), None) + assert function1 is not None + + assert len(function1.requirement_ids) == 2 + assert "r1" in function1.requirement_ids + assert "r2" in function1.requirement_ids + + # Find Function_2 function + function2 = next((f for f in iv.functions if f.name == "Function_2"), None) + assert function2 is not None + + # Find do_smth interface + do_smth = next( + (pi for pi in function2.provided_interfaces if pi.name == "do_smth"), None + ) + assert do_smth is not None + assert len(do_smth.requirement_ids) == 1 + assert "r5" in do_smth.requirement_ids From aa3f4c9ffab39509afaaee065fe9a8243cb48680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Mon, 8 Dec 2025 14:24:32 +0100 Subject: [PATCH 05/28] Ignore output of examples --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index b7faf40..4aef921 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Output in examples +examples/output/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[codz] From d271cc81a3503b0767d45f276ee0de14a9fe2038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Mon, 8 Dec 2025 14:57:01 +0100 Subject: [PATCH 06/28] Added example of generating traces --- examples/generate_traces_list.sh | 4 ++ examples/requirements.tmplt | 69 ++++++++++++++++++++++++++++++++ templateprocessor/cli.py | 44 ++++++++++---------- 3 files changed, 96 insertions(+), 21 deletions(-) create mode 100755 examples/generate_traces_list.sh create mode 100644 examples/requirements.tmplt diff --git a/examples/generate_traces_list.sh b/examples/generate_traces_list.sh new file mode 100755 index 0000000..eac76be --- /dev/null +++ b/examples/generate_traces_list.sh @@ -0,0 +1,4 @@ +#!/bin/bash +mkdir -p output +template-processor --verbosity info --iv ../data/requirements.iv.xml -o output -t requirements.tmplt +pandoc --pdf-engine=pdfroff --output=output/requirements.pdf output/requirements.md \ No newline at end of file diff --git a/examples/requirements.tmplt b/examples/requirements.tmplt new file mode 100644 index 0000000..fc90a2b --- /dev/null +++ b/examples/requirements.tmplt @@ -0,0 +1,69 @@ +<% +# Build requirement ID to component mapping +req_to_comp = {} +for func in interface_view.functions: + # Add function-level requirements + for req_id in func.requirement_ids: + if req_id not in req_to_comp: + req_to_comp[req_id] = [] + req_to_comp[req_id].append((func.name, None)) + + # Add provided interface requirements + for pi in func.provided_interfaces: + for req_id in pi.requirement_ids: + if req_id not in req_to_comp: + req_to_comp[req_id] = [] + req_to_comp[req_id].append((func.name, f"{pi.name} (PI)")) + + # Add required interface requirements + for ri in func.required_interfaces: + for req_id in ri.requirement_ids: + if req_id not in req_to_comp: + req_to_comp[req_id] = [] + req_to_comp[req_id].append((func.name, f"{ri.name} (RI)")) + +# Sort by requirement ID +sorted_req_ids = sorted(req_to_comp.keys()) +%> + +## Forward trace ## + +### Requirement to Component Traces + +| Requirement ID | Component | Interface | +|----------------|----------------------|-----------| +% for req_id in sorted_req_ids: +<% +if not req_id: + continue +%> \ +% for comp_name, iface_name in req_to_comp[req_id]: +<% +iface_display = iface_name if iface_name else "-" +%> \ +| ${req_id or "-"} | ${comp_name} | ${iface_display} | +% endfor +% endfor + +## Backward trace ## + +### Component to Requirement Traces + +| Component | Interface | Requirement IDs | +|----------------------|-----------|-----------------| +% for func in interface_view.functions: +<% + # Collect function-level requirements + func_reqs = ', '.join(sorted(func.requirement_ids)) if func.requirement_ids else "-" +%>| ${func.name} | - | ${func_reqs} | +% for pi in func.provided_interfaces: +<% + pi_reqs = ', '.join(sorted(pi.requirement_ids)) if pi.requirement_ids else "-" +%>| ${func.name} | ${pi.name} (PI) | ${pi_reqs} | +% endfor +% for ri in func.required_interfaces: +<% + ri_reqs = ', '.join(sorted(ri.requirement_ids)) if ri.requirement_ids else "-" +%>| ${func.name} | ${ri.name} (RI) | ${ri_reqs} | +% endfor +% endfor \ No newline at end of file diff --git a/templateprocessor/cli.py b/templateprocessor/cli.py index ea945e9..fadb10b 100644 --- a/templateprocessor/cli.py +++ b/templateprocessor/cli.py @@ -100,30 +100,32 @@ def main(): logging.info(f"Reading Interface View from {args.iv}") iv = IVReader().read(args.iv) if args.iv else InterfaceView() sots = {} - so_reader = SOReader() - for sot_file in args.sos: - logging.info(f"Reading System Objects from {sot_file}") - name = Path(sot_file).stem - logging.debug(f"-SOT name: {name}") - sos = so_reader.read(sot_file) - sots[name] = sos + if args.sos: + so_reader = SOReader() + for sot_file in args.sos: + logging.info(f"Reading System Objects from {sot_file}") + name = Path(sot_file).stem + logging.debug(f"-SOT name: {name}") + sos = so_reader.read(sot_file) + sots[name] = sos instantiator = TemplateInstantiator(iv, sots) - for template_file in args.template: - logging.info(f"Processing template {template_file}") - name = Path(template_file).stem - logging.debug(f"Base name: {name}") - logging.debug(f"Reading template {template_file}") - with open(template_file, "r") as f: - template = f.read() - logging.debug(f"Instantiating template:\n {template}") - instantiated_template = instantiator.instantiate(template, "") - logging.debug(f"Instantiation:\n {instantiated_template}") - output = Path(args.output) / f"{name}.md" - logging.debug(f"Saving to {output}") - with open(output, "w") as f: - f.write(instantiated_template) + if args.template: + for template_file in args.template: + logging.info(f"Processing template {template_file}") + name = Path(template_file).stem + logging.debug(f"Base name: {name}") + logging.debug(f"Reading template {template_file}") + with open(template_file, "r") as f: + template = f.read() + logging.debug(f"Instantiating template:\n {template}") + instantiated_template = instantiator.instantiate(template, "") + logging.debug(f"Instantiation:\n {instantiated_template}") + output = Path(args.output) / f"{name}.md" + logging.debug(f"Saving to {output}") + with open(output, "w") as f: + f.write(instantiated_template) return 0 From 4de0a3f5d87f194ce9f6b942941e82929a7257af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Mon, 8 Dec 2025 15:55:33 +0100 Subject: [PATCH 07/28] Improved trace example --- examples/requirements.tmplt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/requirements.tmplt b/examples/requirements.tmplt index fc90a2b..d51881a 100644 --- a/examples/requirements.tmplt +++ b/examples/requirements.tmplt @@ -26,9 +26,9 @@ for func in interface_view.functions: sorted_req_ids = sorted(req_to_comp.keys()) %> -## Forward trace ## +${'##'} Forward trace ${'##'} -### Requirement to Component Traces +${'###'} Requirement to Component Traces | Requirement ID | Component | Interface | |----------------|----------------------|-----------| @@ -45,9 +45,9 @@ iface_display = iface_name if iface_name else "-" % endfor % endfor -## Backward trace ## +${'##'} Backward trace ${'##'} -### Component to Requirement Traces +${'###'} Component to Requirement Traces | Component | Interface | Requirement IDs | |----------------------|-----------|-----------------| From f5ef5f87bdf14713917c10b4f8b4e439d69b84e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Mon, 8 Dec 2025 15:55:56 +0100 Subject: [PATCH 08/28] Added placeholders for the templates --- .../ecss-e-st-40c_4_1_software_static_architecture.tmplt | 1 + .../ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt | 1 + data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt | 0 data/ecss-template/ecss-e-st-40c_5_1_software_design.tmplt | 0 data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt | 0 .../ecss-e-st-40c_5_3_software_copmponents_design.tmplt | 0 .../ecss-e-st-40c_5_4_aspects_of_each_component.tmplt | 1 + .../ecss-e-st-40c_5_5_internal_interface_design.tmplt | 1 + .../ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt | 1 + 9 files changed, 5 insertions(+) create mode 100644 data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt create mode 100644 data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt create mode 100644 data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt create mode 100644 data/ecss-template/ecss-e-st-40c_5_1_software_design.tmplt create mode 100644 data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt create mode 100644 data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt create mode 100644 data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt create mode 100644 data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt create mode 100644 data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt diff --git a/data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt b/data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt new file mode 100644 index 0000000..1ec43e0 --- /dev/null +++ b/data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt @@ -0,0 +1 @@ +TODO architecture of the software item, including relationship between its major components \ No newline at end of file diff --git a/data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt b/data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt new file mode 100644 index 0000000..41e7ba6 --- /dev/null +++ b/data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt @@ -0,0 +1 @@ +TODO real-time constraints \ No newline at end of file diff --git a/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt b/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt new file mode 100644 index 0000000..e69de29 diff --git a/data/ecss-template/ecss-e-st-40c_5_1_software_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_1_software_design.tmplt new file mode 100644 index 0000000..e69de29 diff --git a/data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt b/data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt new file mode 100644 index 0000000..e69de29 diff --git a/data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt new file mode 100644 index 0000000..e69de29 diff --git a/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt b/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt new file mode 100644 index 0000000..c316449 --- /dev/null +++ b/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt @@ -0,0 +1 @@ +TODO detailed description of each component \ No newline at end of file diff --git a/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt new file mode 100644 index 0000000..e89bed5 --- /dev/null +++ b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt @@ -0,0 +1 @@ +TODO internal interfaces, including data \ No newline at end of file diff --git a/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt b/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt new file mode 100644 index 0000000..797287b --- /dev/null +++ b/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt @@ -0,0 +1 @@ +TODO forward and backward traceability \ No newline at end of file From 384b7ca0907b1947772edecc3d7594334da016f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Mon, 8 Dec 2025 17:47:27 +0100 Subject: [PATCH 09/28] Added DV example --- examples/dv.tmplt | 20 ++++++++++++++++++++ examples/generate_dv.sh | 4 ++++ templateprocessor/cli.py | 14 +++++++++++++- templateprocessor/templateinstantiator.py | 5 +++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 examples/dv.tmplt create mode 100755 examples/generate_dv.sh diff --git a/examples/dv.tmplt b/examples/dv.tmplt new file mode 100644 index 0000000..fe01d50 --- /dev/null +++ b/examples/dv.tmplt @@ -0,0 +1,20 @@ +${"#"} Deployment View +% for node in deployment_view.nodes: +% for partition in node.partitions: + +${"##"} ${partition.name}: +% for func in partition.functions: +- ${func.name} +% endfor +% endfor +% endfor + +${"##"} Connections: + +% for connection in deployment_view.connections: +${connection.from_node} <-> ${connection.to_node} +% for message in connection.messages: + +- ${message.from_function}.${message.from_interface} -> ${message.to_function}.${message.to_interface} +% endfor +% endfor diff --git a/examples/generate_dv.sh b/examples/generate_dv.sh new file mode 100755 index 0000000..f61098d --- /dev/null +++ b/examples/generate_dv.sh @@ -0,0 +1,4 @@ +#!/bin/bash +mkdir -p output +template-processor --verbosity info --dv ../data/deploymentview.dv.xml -o output -t dv.tmplt +pandoc --pdf-engine=pdfroff --output=output/dv.pdf output/dv.md \ No newline at end of file diff --git a/templateprocessor/cli.py b/templateprocessor/cli.py index fadb10b..45d2ffa 100644 --- a/templateprocessor/cli.py +++ b/templateprocessor/cli.py @@ -9,9 +9,11 @@ import sys from templateprocessor import __version__ from templateprocessor.iv import InterfaceView +from templateprocessor.dv import DeploymentView from templateprocessor.templateinstantiator import TemplateInstantiator from templateprocessor.ivreader import IVReader from templateprocessor.soreader import SOReader +from templateprocessor.dvreader import DVReader def parse_arguments() -> argparse.Namespace: @@ -31,6 +33,13 @@ def parse_arguments() -> argparse.Namespace: metavar="FILE", ) + parser.add_argument( + "-d", + "--dv", + help="Input Deployment View", + metavar="FILE", + ) + parser.add_argument( "-s", "--sos", @@ -93,12 +102,15 @@ def main(): logging.info("Template Processor") logging.debug(f"Interface View: {args.iv}") + logging.debug(f"Deployment View: {args.dv}") logging.debug(f"System Objects: {args.sos}") logging.debug(f"Templates: {args.template}") logging.debug(f"Output Directory: {args.output}") logging.info(f"Reading Interface View from {args.iv}") iv = IVReader().read(args.iv) if args.iv else InterfaceView() + logging.info(f"Reading Deployment View from {args.dv}") + dv = DVReader().read(args.dv) if args.dv else DeploymentView() sots = {} if args.sos: so_reader = SOReader() @@ -109,7 +121,7 @@ def main(): sos = so_reader.read(sot_file) sots[name] = sos - instantiator = TemplateInstantiator(iv, sots) + instantiator = TemplateInstantiator(iv, dv, sots) if args.template: for template_file in args.template: diff --git a/templateprocessor/templateinstantiator.py b/templateprocessor/templateinstantiator.py index 8883eb9..60bfb9f 100644 --- a/templateprocessor/templateinstantiator.py +++ b/templateprocessor/templateinstantiator.py @@ -5,6 +5,7 @@ """ from templateprocessor.iv import InterfaceView +from templateprocessor.dv import DeploymentView from templateprocessor.so import SystemObjectType, SystemObject from typing import List, Dict from mako.template import Template @@ -17,14 +18,17 @@ class TemplateInstantiator: system_object_types: Dict[str, SystemObjectType] = dict() interface_view: InterfaceView + deployment_view: DeploymentView def __init__( self, interface_view: InterfaceView, + deployment_view: DeploymentView, system_object_types: Dict[str, SystemObjectType], ): self.system_object_types = system_object_types self.interface_view = interface_view + self.deployment_view = deployment_view def instantiate(self, template: str, context_directory: str) -> str: mako_template = Template(text=template, module_directory=context_directory) @@ -32,6 +36,7 @@ def instantiate(self, template: str, context_directory: str) -> str: context = { "system_object_types": self.system_object_types, "interface_view": self.interface_view, + "deployment_view": self.deployment_view, } instantiated_text = str(mako_template.render(**context)) From 59166c0d55a89c98bda909a8c4f51715a35adbf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Mon, 8 Dec 2025 18:19:58 +0100 Subject: [PATCH 10/28] Added ECSS-like requirement trace matrices --- ...-e-st-40c_6_requirement_traceability.tmplt | 48 ++++++++++++++++++- examples/generate_ecss_demo.sh | 4 ++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100755 examples/generate_ecss_demo.sh diff --git a/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt b/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt index 797287b..6d63c33 100644 --- a/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt +++ b/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt @@ -1 +1,47 @@ -TODO forward and backward traceability \ No newline at end of file +<% +## Data Initialization +# Get all functions + +funcs = [] +def get_function_children(func): + result = [] + if func.nested_functions: + for nested in func.nested_functions: + result.append(nested) + result.extend(get_function_children(nested)) + return result + +for func in interface_view.functions: + funcs.append(func) + funcs.extend(get_function_children(func)) + +# Get all requirements +req_map = {} +for func in funcs: + for req_id in func.requirement_ids: + if not req_id in req_map: + req_map[req_id] = [] + req_map[req_id].append(func) + +req_ids = sorted(req_map.keys()) # This can be sourced from an external (e.g., JSON) file, if needed +%> + +${"#"} Forward traceability matrix + +| Requirement ID | Components | +| - | - | +% for id in req_ids: +<% +if not id: + continue +%> \ +| ${id} | ${",".join([func.name for func in req_map[id]])} | +% endfor + +${"#"} Backward traceability matrix + +| Component | Requirement IDs | +| - | - | +% for func in funcs: +| ${func.name} | ${",".join(func.requirement_ids)} | +% endfor diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh new file mode 100755 index 0000000..e130807 --- /dev/null +++ b/examples/generate_ecss_demo.sh @@ -0,0 +1,4 @@ +#!/bin/bash +mkdir -p output +template-processor --verbosity info --iv ../data/requirements.iv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_6_requirement_traceability.pdf output/ecss-e-st-40c_6_requirement_traceability.md From 01a440d0c71176e1d2259a89125216257b6dc05a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Mon, 8 Dec 2025 18:49:11 +0100 Subject: [PATCH 11/28] Aspects of each component POC --- ...st-40c_5_4_aspects_of_each_component.tmplt | 62 ++++++++++++++++++- examples/generate_ecss_demo.sh | 2 + 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt b/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt index c316449..04da6d9 100644 --- a/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt +++ b/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt @@ -1 +1,61 @@ -TODO detailed description of each component \ No newline at end of file +<% +## Data Initialization +# Get all functions + +funcs = [] +def get_function_children(func): + result = [] + if func.nested_functions: + for nested in func.nested_functions: + result.append(nested) + result.extend(get_function_children(nested)) + return result + +for func in interface_view.functions: + funcs.append(func) + funcs.extend(get_function_children(func)) + +funcs.sort(key=lambda f: f.name.lower()) + +%> + +The below chapters summarize aspects of each component. + +% for func in funcs: + +${"#"} ${func.name} + +Component Identifier: ${func.name} + +Type: TASTE Function + +Purpose: N/A + +Subordinates: ${",".join([child.name for child in func.nested_functions])} + +Dependencies: N/A + +Required Interfaces: + +% for ri in func.required_interfaces: + +- [${str(ri.kind.value).lower()}] ${ri.name} +% endfor + +Provided Interfaces: +% for pi in func.provided_interfaces: + +- [${str(pi.kind.value).lower()}] ${pi.name} +% endfor + +Resources: N/A + +References: N/A + +Data: N/A + +Backward Requirement Trace: ${", ".join(func.requirement_ids) } + +Forward Requirement Trace: Described in Chapter 6 + +% endfor diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh index e130807..b168264 100755 --- a/examples/generate_ecss_demo.sh +++ b/examples/generate_ecss_demo.sh @@ -2,3 +2,5 @@ mkdir -p output template-processor --verbosity info --iv ../data/requirements.iv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_6_requirement_traceability.pdf output/ecss-e-st-40c_6_requirement_traceability.md +template-processor --verbosity info --iv ../data/simple.iv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_4_aspects_of_each_component.pdf output/ecss-e-st-40c_5_4_aspects_of_each_component.md From dd826ce1c6eac16137ef99efe4051846da2ffa3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Tue, 9 Dec 2025 15:29:24 +0100 Subject: [PATCH 12/28] Updated template instantiation tests with DV --- tests/test_templateinstantiator.py | 295 ++++++++++++++++++++++++++++- 1 file changed, 287 insertions(+), 8 deletions(-) diff --git a/tests/test_templateinstantiator.py b/tests/test_templateinstantiator.py index d25c572..b82d0ec 100644 --- a/tests/test_templateinstantiator.py +++ b/tests/test_templateinstantiator.py @@ -19,6 +19,15 @@ Encoding, ) from templateprocessor.so import SystemObjectType, SystemObject +from templateprocessor.dv import ( + DeploymentView, + Node, + Partition, + DeploymentFunction, + Device, + Connection, + Message, +) class TestTemplateInstantiator: @@ -95,12 +104,117 @@ def create_sample_system_object_types() -> Dict[str, SystemObjectType]: return sots + @staticmethod + def create_sample_deployment_view() -> DeploymentView: + """Create a sample DeploymentView for testing.""" + dv = DeploymentView( + version="1.2", + ui_file="test_deployment.ui.xml", + creator_hash="test_creator", + modifier_hash="test_modifier", + ) + + # Create first node (x86 Linux) + node1 = Node( + id="n1", + name="x86 Linux C++_1", + type="ocarina_processors_x86::x86.generic_linux", + node_label="Node_1", + namespace="ocarina_processors_x86", + requirement_ids=["r20", "r21"], + ) + + # Add partition to node1 + partition1 = Partition( + id="p1", + name="test_partition_1", + ) + + # Add functions to partition + func1 = DeploymentFunction( + id="f1", + name="TestFunction", + path="/test/path/function1", + ) + partition1.functions.append(func1) + node1.partitions.append(partition1) + + # Add device to node1 + device1 = Device( + id="d1", + name="uart0", + requires_bus_access="UART", + port="uart0", + asn1file="test.asn", + asn1type="TestType", + asn1module="TestModule", + namespace="test_namespace", + extends="BaseDevice", + impl_extends="BaseImpl", + bus_namespace="uart_namespace", + ) + node1.devices.append(device1) + + # Create second node (ARM RTEMS) + node2 = Node( + id="n2", + name="SAM V71 RTEMS N7S_1", + type="ocarina_processors_arm::samv71.rtems", + node_label="Node_2", + namespace="ocarina_processors_arm", + ) + + # Add partition to node2 + partition2 = Partition( + id="p2", + name="test_partition_2", + ) + + func2 = DeploymentFunction( + id="f2", + name="SensorFunction", + path="/test/path/function2", + ) + partition2.functions.append(func2) + node2.partitions.append(partition2) + + # Add nodes to deployment view + dv.nodes.append(node1) + dv.nodes.append(node2) + + # Create connection between nodes + connection = Connection( + id="c1", + name="UartLink", + from_node="n1", + from_port="uart0", + to_bus="UART", + to_node="n2", + to_port="uart0", + ) + + # Add messages to connection + msg1 = Message( + id="m1", + name="DataMessage", + from_function="TestFunction", + from_interface="test_pi", + to_function="SensorFunction", + to_interface="sensor_ri", + ) + connection.messages.append(msg1) + + dv.connections.append(connection) + + return dv + def test_instantiator_initialization(self): """Test TemplateInstantiator initialization.""" iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types) assert instantiator.interface_view == iv assert instantiator.system_object_types == so_types @@ -109,8 +223,9 @@ def test_instantiator_initialization(self): def test_instantiate_simple_template(self): """Test instantiating a simple template.""" iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types) template = "Hello World!" @@ -122,8 +237,9 @@ def test_instantiate_simple_template(self): def test_instantiate_template_with_interface_view(self): """Test instantiating a template that uses Interface View.""" iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types) template = """Interface View version: ${interface_view.version} ASN1 file: ${interface_view.asn1file} @@ -139,8 +255,9 @@ def test_instantiate_template_with_interface_view(self): def test_instantiate_template_with_function_details(self): """Test instantiating a template that accesses Function details.""" iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types) template = """% for func in interface_view.functions: Function: ${func.name} @@ -160,8 +277,9 @@ def test_instantiate_template_with_function_details(self): def test_instantiate_template_with_system_object_types(self): """Test instantiating a template that uses System Object Types.""" iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types) template = """Number of System Object Types: ${len(system_object_types)} % for name, so_type in system_object_types.items(): @@ -180,8 +298,9 @@ def test_instantiate_template_with_system_object_types(self): def test_instantiate_template_with_system_object_instances(self): """Test instantiating a template that accesses System Object Type instances.""" iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types) template = """% for name, so_type in system_object_types.items(): [${name}] @@ -208,10 +327,11 @@ def test_instantiate_template_with_system_object_instances(self): def test_instantiate_template_with_empty_data(self): """Test instantiating a template with empty Interface View and no System Objects.""" iv = InterfaceView(version="1.0", asn1file="", uiFile="", modifierHash="") + dv = self.create_sample_deployment_view() so_types = {} - instantiator = TemplateInstantiator(iv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types) template = """Version: ${interface_view.version} Functions: ${len(interface_view.functions)} @@ -227,8 +347,9 @@ def test_instantiate_template_with_empty_data(self): def test_instantiate_template_with_python_expressions(self): """Test instantiating a template with Python expressions.""" iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types) template = """<% total_interfaces = sum(len(f.provided_interfaces) + len(f.required_interfaces) for f in interface_view.functions) @@ -242,3 +363,161 @@ def test_instantiate_template_with_python_expressions(self): assert "Total Interfaces: 2" in result assert "Total System Object Instances: 4" in result + + def test_instantiate_template_with_deployment_view(self): + """Test instantiating a template that uses Deployment View.""" + iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, dv, so_types) + + template = """Deployment View version: ${deployment_view.version} +UI file: ${deployment_view.ui_file} +Number of nodes: ${len(deployment_view.nodes)} +Number of connections: ${len(deployment_view.connections)}""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Deployment View version: 1.2" in result + assert "UI file: test_deployment.ui.xml" in result + assert "Number of nodes: 2" in result + assert "Number of connections: 1" in result + + def test_instantiate_template_with_deployment_nodes(self): + """Test instantiating a template that accesses Deployment View nodes.""" + iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, dv, so_types) + + template = """% for node in deployment_view.nodes: +Node: ${node.name} +Type: ${node.type} +Label: ${node.node_label} +Namespace: ${node.namespace} +Partitions: ${len(node.partitions)} +Devices: ${len(node.devices)} +% endfor""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Node: x86 Linux C++_1" in result + assert "Type: ocarina_processors_x86::x86.generic_linux" in result + assert "Node: SAM V71 RTEMS N7S_1" in result + assert "Type: ocarina_processors_arm::samv71.rtems" in result + assert "Partitions: 1" in result + assert "Devices: 1" in result + + def test_instantiate_template_with_partitions_and_functions(self): + """Test instantiating a template that accesses partitions and deployed functions.""" + iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, dv, so_types) + + template = """% for node in deployment_view.nodes: +${node.name} +% for partition in node.partitions: +Partition: ${partition.name} +% for func in partition.functions: +- Function: ${func.name} (${func.id}) + Path: ${func.path} +% endfor +% endfor +% endfor""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "x86 Linux C++_1" in result + assert "Partition: test_partition_1" in result + assert "- Function: TestFunction (f1)" in result + assert "Path: /test/path/function1" in result + assert "SAM V71 RTEMS N7S_1" in result + assert "Partition: test_partition_2" in result + assert "- Function: SensorFunction (f2)" in result + assert "Path: /test/path/function2" in result + + def test_instantiate_template_with_devices(self): + """Test instantiating a template that accesses node devices.""" + iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, dv, so_types) + + template = """% for node in deployment_view.nodes: +% if node.devices: +Node: ${node.name} +% for device in node.devices: + Device: ${device.name} + - Port: ${device.port} + - Bus: ${device.requires_bus_access} + - ASN1 Type: ${device.asn1type} +% endfor +% endif +% endfor""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Node: x86 Linux C++_1" in result + assert "Device: uart0" in result + assert "- Port: uart0" in result + assert "- Bus: UART" in result + assert "- ASN1 Type: TestType" in result + + def test_instantiate_template_with_connections(self): + """Test instantiating a template that accesses connections and messages.""" + iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, dv, so_types) + + template = """% for conn in deployment_view.connections: +Connection: ${conn.name} + From: ${conn.from_node}:${conn.from_port} + To: ${conn.to_node}:${conn.to_port} + Bus: ${conn.to_bus} + Messages: ${len(conn.messages)} +% for msg in conn.messages: + - ${msg.name}: ${msg.from_function}.${msg.from_interface} -> ${msg.to_function}.${msg.to_interface} +% endfor +% endfor""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Connection: UartLink" in result + assert "From: n1:uart0" in result + assert "To: n2:uart0" in result + assert "Bus: UART" in result + assert "Messages: 1" in result + assert ( + "- DataMessage: TestFunction.test_pi -> SensorFunction.sensor_ri" in result + ) + + def test_instantiate_template_with_node_requirements(self): + """Test instantiating a template that accesses node requirement IDs.""" + iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() + so_types = self.create_sample_system_object_types() + instantiator = TemplateInstantiator(iv, dv, so_types) + + template = """% for node in deployment_view.nodes: +Node: ${node.name} +% if node.requirement_ids: + Requirements: ${', '.join(node.requirement_ids)} +% else: + Requirements: None +% endif +% endfor""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Node: x86 Linux C++_1" in result + assert "Requirements: r20, r21" in result + assert "Node: SAM V71 RTEMS N7S_1" in result + assert "Requirements: None" in result From 1465314eb3509efe9eed74bd5468bbbbd00c7386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Tue, 9 Dec 2025 18:25:17 +0100 Subject: [PATCH 13/28] Added demo-project --- examples/demo-project/.gitignore | 6 + examples/demo-project/Makefile | 144 +++++ examples/demo-project/Makefile.modelcheck | 36 ++ examples/demo-project/demo-project.acn | 4 + examples/demo-project/demo-project.asn | 21 + examples/demo-project/demo-project.pro | 18 + examples/demo-project/deploymentview.dv.xml | 80 +++ examples/demo-project/deploymentview.ui.xml | 82 +++ examples/demo-project/interfaceview.ui.xml | 218 ++++++++ examples/demo-project/interfaceview.xml | 574 ++++++++++++++++++++ 10 files changed, 1183 insertions(+) create mode 100644 examples/demo-project/.gitignore create mode 100644 examples/demo-project/Makefile create mode 100644 examples/demo-project/Makefile.modelcheck create mode 100644 examples/demo-project/demo-project.acn create mode 100644 examples/demo-project/demo-project.asn create mode 100644 examples/demo-project/demo-project.pro create mode 100755 examples/demo-project/deploymentview.dv.xml create mode 100644 examples/demo-project/deploymentview.ui.xml create mode 100644 examples/demo-project/interfaceview.ui.xml create mode 100644 examples/demo-project/interfaceview.xml diff --git a/examples/demo-project/.gitignore b/examples/demo-project/.gitignore new file mode 100644 index 0000000..30c34ba --- /dev/null +++ b/examples/demo-project/.gitignore @@ -0,0 +1,6 @@ +*.aadl +*.pro.user +*.pro.user.* +.qtc* +# We don't need the details to compile the project, only ASN.1/ACN, IV and DV are of interest +work/ \ No newline at end of file diff --git a/examples/demo-project/Makefile b/examples/demo-project/Makefile new file mode 100644 index 0000000..c9e6d9b --- /dev/null +++ b/examples/demo-project/Makefile @@ -0,0 +1,144 @@ +KAZOO?=kazoo +SPACECREATOR?=spacecreator.AppImage + +# Here you can specify custom compiler/linker flags, and add folders containing +# external code you want to compile and link for a specific partition. +# Use upper case for the partition name: +# +# export _USER_CFLAGS=... +# export _USER_LDFLAGS=... +# export _EXTERNAL_SOURCE_PATH=... +# +# NOTE: this can also be done in the Deployment View directly + +# If you need to reset this Makefile to its original state, run: +# $ taste reset + +# Disable the progress bar from taste-update-data-view when building systems +export NO_PROGRESS_BAR=1 + +# Get the list of ASN.1 files from Space Creator project file: +DISTFILES=$(shell qmake demo-project.pro -o /tmp/null 2>&1) +# Exclude system.asn here as the types inside do not changes - only the values in the PID +# enumeration change, but this does not affect the content of DataView.aadl +ASN1_FILES=$(shell find ${DISTFILES} 2>/dev/null | egrep '\.asn$$|\.asn1$$' | grep -v system.asn) + +all: release + +include Makefile.modelcheck + +release: work/glue_release + rm -rf work/glue_debug + rm -rf work/glue_coverage + $(MAKE) -C work check_targets + $(MAKE) -C work + +debug: work/glue_debug + rm -rf work/glue_release + rm -rf work/glue_coverage + $(MAKE) -C work check_targets + $(MAKE) -C work + +coverage: work/glue_coverage + rm -rf work/glue_release + rm -rf work/glue_debug + $(MAKE) -C work check_targets + $(MAKE) -C work + +# To build and run the system type e.g. 'make debug run' +run: + $(MAKE) -C work run + +# To run Cheddar/Marzhin for scheduling analysis, type 'make edit_cv' +edit_cv: + $(MAKE) -C work run_cv + +# Simulation target (experimental - for systems made of SDL functions only) +simu: + if [ -f work/glue_debug ] || [ -f work/glue_release ] || [ -f work/glue_coverage ]; then $(MAKE) clean; fi + $(MAKE) interfaceview work/glue_simu + $(MAKE) -C work + $(MAKE) -C work/simulation -f Makefile.Simulation simu + +# Simulation and model checking: shortcut to create observer.asn +observer_dataview: + $(MAKE) DataView.aadl + $(MAKE) InterfaceView.aadl + $(MAKE) interfaceview + $(KAZOO) --glue -t SIMU + $(MAKE) -C work dataview/dataview-uniq.asn + $(MAKE) -C work/build -f Makefile.taste observer.asn + +skeletons: + $(MAKE) work/skeletons_built + +work/skeletons_built: InterfaceView.aadl DataView.aadl + $(KAZOO) --gw -o work + $(MAKE) -C work dataview simulink_skeletons + touch DataView.aadl # to avoid rebuilds due to new system.asn + touch $@ + +work/glue_simu: InterfaceView.aadl DataView.aadl + $(KAZOO) -t SIMU --glue --gw + $(MAKE) -C work dataview + touch DataView.aadl + touch $@ + +work/glue_release: InterfaceView.aadl DeploymentView.aadl DataView.aadl + sed -i 's/CoverageEnabled => true/CoverageEnabled => false/g' DeploymentView.aadl || : + $(KAZOO) -p --glue --gw -o work + touch DataView.aadl + touch $@ + +work/glue_debug: InterfaceView.aadl DeploymentView.aadl DataView.aadl + sed -i 's/CoverageEnabled => true/CoverageEnabled => false/g' DeploymentView.aadl || : + $(KAZOO) --debug -p --glue --gw -o work + touch DataView.aadl + touch $@ + +work/glue_coverage: InterfaceView.aadl DeploymentView.aadl DataView.aadl + sed -i 's/CoverageEnabled => false/CoverageEnabled => true/g' DeploymentView.aadl || : + $(KAZOO) --debug -p --glue --gw -o work + touch DataView.aadl + touch $@ + +InterfaceView.aadl: interfaceview.xml + $(SPACECREATOR) --aadlconverter -o $^ -t $(shell taste-config --prefix)/share/xml2aadl/interfaceview.tmplt -x $@ + +%: %.dv.xml Default_Deployment.aadl + # Build using deployment view $^ + @# We must update the .aadl only if the dv.xml file has changed (more recent timestamp) + if [ $< -nt $@.aadl ]; then $(SPACECREATOR) --dvconverter -o $< -t $(shell taste-config --prefix)/share/dv2aadl/deploymentview.tmplt -x $@.aadl; fi; + rsync --checksum $@.aadl DeploymentView.aadl + +interfaceview: Default_Deployment.aadl + # Build when no deployment view is open - use default + rsync --checksum $< DeploymentView.aadl + +Default_Deployment.aadl: interfaceview.xml + # Create/update a default deployment view for Linux target, if none other is provided + $(SPACECREATOR) --aadlconverter -o $^ -t $(shell taste-config --prefix)/share/xml2dv/interfaceview.tmplt -x $@ || exit 1 + rsync --checksum $@ DeploymentView.aadl + +DeploymentView.aadl: Default_Deployment.aadl + +DataView.aadl: ${ASN1_FILES} + $(info Generating/Updating DataView.aadl) + taste-update-data-view $^ work/system.asn + +clean: + rm -rf work/build work/dataview work/glue_simu + rm -f *.aadl # Interface and Deployment views in AADL are generated + rm -f work/glue_release work/glue_debug work/glue_coverage work/skeletons_built + find work -type d -name "wrappers" -exec rm -rf {} + || : + find work -type d -name "*_GUI" -exec rm -rf {} + || : + find work -type d -path "*/QGenC/xmi" -exec rm -rf {} + || : + find work -type d -path "*/QGenC/src/.qgeninfo" -exec rm -rf {} + || : + find work -type d -path "*/QGenC/src/slprj" -exec rm -rf {} + || : + find work -type f -path "*/QGenC/src/built" -exec rm -f {} + || : + find work -type f -path "*/QGenC/src/*.slxc" -exec rm -f {} + || : + find work -type f -path "*/QGenC/src/*.h" -not -name "simulink_definition_of_types.h" -not -name "*_invoke_ri.h" -exec rm -f {} + || : + find work -type f -path "*/QGenC/src/*.c" -exec rm -f {} + || : + +.PHONY: clean release debug coverage skeletons simu run simulink_skeletons + diff --git a/examples/demo-project/Makefile.modelcheck b/examples/demo-project/Makefile.modelcheck new file mode 100644 index 0000000..f07a8f9 --- /dev/null +++ b/examples/demo-project/Makefile.modelcheck @@ -0,0 +1,36 @@ +model-check: InterfaceView.aadl DeploymentView.aadl DataView.aadl + $(KAZOO) -gw --glue -t MOCHECK + $(MAKE) -C work model-check + +create-obs: work/modelchecking/properties work/modelchecking observer_dataview + mkdir -p work/modelchecking/properties/$(NAME) + make -C work obs-skeleton NAME=$(NAME) + +create-msc: work/modelchecking/properties work/modelchecking observer_dataview + mkdir -p work/modelchecking/properties/$(NAME) + make -C work msc-skeleton NAME=$(NAME) + +create-bsc: work/modelchecking/properties work/modelchecking observer_dataview + mkdir -p work/modelchecking/properties/$(NAME) + make -C work bsc-skeleton NAME=$(NAME) + +work/modelchecking/properties: + mkdir -p work/modelchecking/properties + +work/modelchecking: + mkdir -p work/modelchecking + +create-subtype: work/modelchecking/subtypes work/modelchecking + find work/ -path work/binaries -prune -o -name subtype_*.asn -exec cat {} \; > work/modelchecking/subtypes/$(NAME).asn + +work/modelchecking/subtypes: + mkdir -p work/modelchecking/subtypes + +# Native model cheker target (experimental - for systems made of SDL functions only) +native_modelchecker: + if [ -f work/glue_debug ] || [ -f work/glue_release ] || [ -f work/glue_coverage ]; then $(MAKE) clean; fi + $(MAKE) interfaceview work/glue_simu + $(MAKE) -C work + $(MAKE) -C work/simulation -f Makefile.Simulation modelcheck + cd work/simulation && ./modelcheck + diff --git a/examples/demo-project/demo-project.acn b/examples/demo-project/demo-project.acn new file mode 100644 index 0000000..b87156b --- /dev/null +++ b/examples/demo-project/demo-project.acn @@ -0,0 +1,4 @@ +DEMO-PROJECT-DATAVIEW DEFINITIONS ::= BEGIN + +END + diff --git a/examples/demo-project/demo-project.asn b/examples/demo-project/demo-project.asn new file mode 100644 index 0000000..83057fa --- /dev/null +++ b/examples/demo-project/demo-project.asn @@ -0,0 +1,21 @@ +DEMO-PROJECT-DATAVIEW DEFINITIONS ::= +BEGIN + + MyInteger ::= INTEGER (0 .. 10000) + +-- Dummy types with different names for documentation purposes + + TC ::= INTEGER (0..10) + TM ::= INTEGER (0..10) + + Request ::= INTEGER (0..10) + Response ::= INTEGER (0..10) + + ParameterValue ::= INTEGER (0..10) + + Cmd ::= INTEGER (0..10) + + RawData ::= INTEGER (0..10) + +END + diff --git a/examples/demo-project/demo-project.pro b/examples/demo-project/demo-project.pro new file mode 100644 index 0000000..2978d60 --- /dev/null +++ b/examples/demo-project/demo-project.pro @@ -0,0 +1,18 @@ +TEMPLATE = lib +CONFIG -= qt +CONFIG += generateC + +DISTFILES += $(HOME)/tool-inst/share/taste-types/taste-types.asn \ + deploymentview.dv.xml +DISTFILES += demo-project.msc +DISTFILES += interfaceview.xml +DISTFILES += work/binaries/*.msc +DISTFILES += work/binaries/coverage/index.html +DISTFILES += work/binaries/filters +DISTFILES += work/system.asn + +DISTFILES += demo-project.asn +DISTFILES += demo-project.acn +include(work/taste.pro) +message($$DISTFILES) + diff --git a/examples/demo-project/deploymentview.dv.xml b/examples/demo-project/deploymentview.dv.xml new file mode 100755 index 0000000..8946e23 --- /dev/null +++ b/examples/demo-project/deploymentview.dv.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/demo-project/deploymentview.ui.xml b/examples/demo-project/deploymentview.ui.xml new file mode 100644 index 0000000..b3385c4 --- /dev/null +++ b/examples/demo-project/deploymentview.ui.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/demo-project/interfaceview.ui.xml b/examples/demo-project/interfaceview.ui.xml new file mode 100644 index 0000000..c49ca8f --- /dev/null +++ b/examples/demo-project/interfaceview.ui.xml @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/demo-project/interfaceview.xml b/examples/demo-project/interfaceview.xml new file mode 100644 index 0000000..ba2c2e2 --- /dev/null +++ b/examples/demo-project/interfaceview.xml @@ -0,0 +1,574 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 131e8b95449e3ce5626c9a58653023d412f92384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Tue, 9 Dec 2025 18:25:56 +0100 Subject: [PATCH 14/28] Improved traceability template --- ...-e-st-40c_6_requirement_traceability.tmplt | 21 +++++++++++++++++-- examples/generate_ecss_demo.sh | 4 ++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt b/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt index 6d63c33..8081f61 100644 --- a/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt +++ b/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt @@ -15,15 +15,32 @@ for func in interface_view.functions: funcs.append(func) funcs.extend(get_function_children(func)) +# Get functions deployed to the target partition +target_partition_name = "ASW" + +deployed_funcs = [] +target_partition = None +for node in deployment_view.nodes: + for partition in node.partitions: + if partition.name == target_partition_name: + target_partition = partition + +deployed_func_names = [f.name for f in target_partition.functions] +for fun in funcs: + if fun.name in deployed_func_names: + deployed_funcs.append(fun) + + # Get all requirements req_map = {} -for func in funcs: +for func in deployed_funcs: for req_id in func.requirement_ids: if not req_id in req_map: req_map[req_id] = [] req_map[req_id].append(func) req_ids = sorted(req_map.keys()) # This can be sourced from an external (e.g., JSON) file, if needed + %> ${"#"} Forward traceability matrix @@ -42,6 +59,6 @@ ${"#"} Backward traceability matrix | Component | Requirement IDs | | - | - | -% for func in funcs: +% for func in deployed_funcs: | ${func.name} | ${",".join(func.requirement_ids)} | % endfor diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh index b168264..e038108 100755 --- a/examples/generate_ecss_demo.sh +++ b/examples/generate_ecss_demo.sh @@ -1,6 +1,6 @@ #!/bin/bash mkdir -p output -template-processor --verbosity info --iv ../data/requirements.iv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt +template-processor --verbosity info --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_6_requirement_traceability.pdf output/ecss-e-st-40c_6_requirement_traceability.md -template-processor --verbosity info --iv ../data/simple.iv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt +template-processor --verbosity info --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_4_aspects_of_each_component.pdf output/ecss-e-st-40c_5_4_aspects_of_each_component.md From bec64234dfb21bad57d670c97740a9069342034c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Tue, 9 Dec 2025 18:27:18 +0100 Subject: [PATCH 15/28] Added missing languages in IV parsing --- templateprocessor/iv.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templateprocessor/iv.py b/templateprocessor/iv.py index e67865f..0c45d64 100644 --- a/templateprocessor/iv.py +++ b/templateprocessor/iv.py @@ -37,6 +37,8 @@ class Language(str, Enum): CPP = "C++" SIMULINK = "Simulink" QGenc = "QGenc" + GUI = "GUI" + BLACKBOX_C = "Blackbox_C" @dataclass From ab854d6259216aadc7d61b03adcf206d4e0d11c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Tue, 9 Dec 2025 18:58:39 +0100 Subject: [PATCH 16/28] Added custom value support --- ...st-40c_5_4_aspects_of_each_component.tmplt | 21 ++++++++++-- ...-e-st-40c_6_requirement_traceability.tmplt | 3 +- examples/generate_ecss_demo.sh | 4 +-- templateprocessor/cli.py | 32 ++++++++++++++++++- templateprocessor/iv.py | 1 + templateprocessor/ivreader.py | 1 + templateprocessor/templateinstantiator.py | 4 +++ 7 files changed, 58 insertions(+), 8 deletions(-) diff --git a/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt b/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt index 04da6d9..46ce4fb 100644 --- a/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt +++ b/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt @@ -17,19 +17,34 @@ for func in interface_view.functions: funcs.sort(key=lambda f: f.name.lower()) +# Get functions deployed to the target partition +target_partition_name = values["TARGET"] + +deployed_funcs = [] +target_partition = None +for node in deployment_view.nodes: + for partition in node.partitions: + if partition.name == target_partition_name: + target_partition = partition + +deployed_func_names = [f.name for f in target_partition.functions] +for fun in funcs: + if fun.name in deployed_func_names: + deployed_funcs.append(fun) + %> The below chapters summarize aspects of each component. -% for func in funcs: +% for func in deployed_funcs: ${"#"} ${func.name} Component Identifier: ${func.name} -Type: TASTE Function +Type: TASTE ${func.language.value} Function -Purpose: N/A +Purpose: ${func.comment} Subordinates: ${",".join([child.name for child in func.nested_functions])} diff --git a/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt b/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt index 8081f61..816aee3 100644 --- a/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt +++ b/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt @@ -16,7 +16,7 @@ for func in interface_view.functions: funcs.extend(get_function_children(func)) # Get functions deployed to the target partition -target_partition_name = "ASW" +target_partition_name = values["TARGET"] deployed_funcs = [] target_partition = None @@ -30,7 +30,6 @@ for fun in funcs: if fun.name in deployed_func_names: deployed_funcs.append(fun) - # Get all requirements req_map = {} for func in deployed_funcs: diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh index e038108..e57c191 100755 --- a/examples/generate_ecss_demo.sh +++ b/examples/generate_ecss_demo.sh @@ -1,6 +1,6 @@ #!/bin/bash mkdir -p output -template-processor --verbosity info --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_6_requirement_traceability.pdf output/ecss-e-st-40c_6_requirement_traceability.md -template-processor --verbosity info --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_4_aspects_of_each_component.pdf output/ecss-e-st-40c_5_4_aspects_of_each_component.md diff --git a/templateprocessor/cli.py b/templateprocessor/cli.py index 45d2ffa..ba8dadd 100644 --- a/templateprocessor/cli.py +++ b/templateprocessor/cli.py @@ -48,6 +48,13 @@ def parse_arguments() -> argparse.Namespace: action="append", ) + parser.add_argument( + "-v", + "--value", + help="Input values (formatted as name=value pair, e.g., target=ASW)", + action="append", + ) + parser.add_argument( "-t", "--template", @@ -93,6 +100,25 @@ def get_log_level(level_str: str) -> int: return log_levels.get(level_str.lower(), logging.WARNING) +def get_values_dictionary(values: list[str]) -> dict[str, str]: + if not values or not isinstance(values, list): + return {} + result = {} + for pair in values: + if pair.count("=") != 1: + raise Exception( + f"Pair [{pair}] contains incorrect number of name/value separators (=)" + ) + split = pair.split("=") + name = split[0].strip() + value = split[1].strip() + if len(name) == 0: + raise Exception(f"Name in [{pair}] is empty") + # value can be empty + result[name] = value + return result + + def main(): """Main entry point for the Template Processor CLI.""" @@ -104,6 +130,7 @@ def main(): logging.debug(f"Interface View: {args.iv}") logging.debug(f"Deployment View: {args.dv}") logging.debug(f"System Objects: {args.sos}") + logging.debug(f"Values: {args.value}") logging.debug(f"Templates: {args.template}") logging.debug(f"Output Directory: {args.output}") @@ -120,8 +147,11 @@ def main(): logging.debug(f"-SOT name: {name}") sos = so_reader.read(sot_file) sots[name] = sos + logging.info(f"Parsing values from {args.value}") + values = get_values_dictionary(args.value) - instantiator = TemplateInstantiator(iv, dv, sots) + logging.info(f"Instantiating the TemplateInstantiator") + instantiator = TemplateInstantiator(iv, dv, sots, values) if args.template: for template_file in args.template: diff --git a/templateprocessor/iv.py b/templateprocessor/iv.py index 0c45d64..8be8a4e 100644 --- a/templateprocessor/iv.py +++ b/templateprocessor/iv.py @@ -125,6 +125,7 @@ class Function: id: str name: str + comment: str is_type: bool language: Optional[Language] = None default_implementation: str = "default" diff --git a/templateprocessor/ivreader.py b/templateprocessor/ivreader.py index 3557eec..5d4efc7 100644 --- a/templateprocessor/ivreader.py +++ b/templateprocessor/ivreader.py @@ -118,6 +118,7 @@ def _parse_function(self, elem: ET.Element) -> Function: function = Function( id=elem.get("id", ""), name=elem.get("name", ""), + comment=elem.get("Comment", ""), is_type=elem.get("is_type", "NO") == "YES", language=( Language(elem.get("language", "")) if elem.get("language") else None diff --git a/templateprocessor/templateinstantiator.py b/templateprocessor/templateinstantiator.py index 60bfb9f..d2a32b2 100644 --- a/templateprocessor/templateinstantiator.py +++ b/templateprocessor/templateinstantiator.py @@ -17,6 +17,7 @@ class TemplateInstantiator: """ system_object_types: Dict[str, SystemObjectType] = dict() + values: Dict[str, str] = dict() interface_view: InterfaceView deployment_view: DeploymentView @@ -25,10 +26,12 @@ def __init__( interface_view: InterfaceView, deployment_view: DeploymentView, system_object_types: Dict[str, SystemObjectType], + values: Dict[str, str], ): self.system_object_types = system_object_types self.interface_view = interface_view self.deployment_view = deployment_view + self.values = values def instantiate(self, template: str, context_directory: str) -> str: mako_template = Template(text=template, module_directory=context_directory) @@ -37,6 +40,7 @@ def instantiate(self, template: str, context_directory: str) -> str: "system_object_types": self.system_object_types, "interface_view": self.interface_view, "deployment_view": self.deployment_view, + "values": self.values, } instantiated_text = str(mako_template.render(**context)) From dd16df951d53f6bba425bab2f6a9dd962bc44c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Wed, 10 Dec 2025 14:43:00 +0100 Subject: [PATCH 17/28] Added dynamic architecture template and improved interface data model --- ...0c_4_2_software_dynamic_architecture.tmplt | 75 ++- examples/demo-project/interfaceview.ui.xml | 190 ++++---- examples/demo-project/interfaceview.xml | 444 +++++++++--------- examples/generate_ecss_demo.sh | 2 + templateprocessor/iv.py | 1 + templateprocessor/ivreader.py | 1 + 6 files changed, 395 insertions(+), 318 deletions(-) diff --git a/data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt b/data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt index 41e7ba6..adbfb7b 100644 --- a/data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt +++ b/data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt @@ -1 +1,74 @@ -TODO real-time constraints \ No newline at end of file +<% +## Data Initialization +# Get all functions + +funcs = [] +def get_function_children(func): + result = [] + if func.nested_functions: + for nested in func.nested_functions: + result.append(nested) + result.extend(get_function_children(nested)) + return result + +for func in interface_view.functions: + funcs.append(func) + funcs.extend(get_function_children(func)) + +funcs.sort(key=lambda f: f.name.lower()) + +# Get functions deployed to the target partition +target_partition_name = values["TARGET"] + +deployed_funcs = [] +target_partition = None +for node in deployment_view.nodes: + for partition in node.partitions: + if partition.name == target_partition_name: + target_partition = partition + +deployed_func_names = [f.name for f in target_partition.functions] +for fun in funcs: + if fun.name in deployed_func_names: + deployed_funcs.append(fun) + +# Get all threads +threads = [] +for func in deployed_funcs: + for pi in func.provided_interfaces: + if pi.kind.value == "Sporadic" or pi.kind.value == "Cyclic": + threads.append((func, pi)) + +%> + + +The list below summarizes all threads of the user components. +% for (func, pi) in threads: + +${"##"} [${pi.kind.value}] ${func.name}::${pi.name} + +- Description: ${pi.comment} + +- Stack size: ${pi.stack_size} + +% if pi.kind.value == "Sporadic": +- Queue size: ${pi.queue_size} + +% endif +- Priority: ${pi.priority} + +% if pi.kind.value == "Cyclic": +- Period: ${pi.period} + +- Dispatch offset : ${pi.dispatch_offset} +%endif + +% if pi.wcet is not None and pi.wcet != 0: +- WCET: ${pi.wcet} + +%endif +% if pi.miat is not None and pi.miat != 0: +- MIAT: ${pi.miat} + +% endif +% endfor diff --git a/examples/demo-project/interfaceview.ui.xml b/examples/demo-project/interfaceview.ui.xml index c49ca8f..32ec9c8 100644 --- a/examples/demo-project/interfaceview.ui.xml +++ b/examples/demo-project/interfaceview.ui.xml @@ -1,52 +1,26 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + @@ -58,26 +32,26 @@ + + - - + + - - @@ -104,115 +78,141 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + \ No newline at end of file diff --git a/examples/demo-project/interfaceview.xml b/examples/demo-project/interfaceview.xml index ba2c2e2..4e7d949 100644 --- a/examples/demo-project/interfaceview.xml +++ b/examples/demo-project/interfaceview.xml @@ -1,116 +1,50 @@ - + - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + @@ -121,9 +55,9 @@ - + - + @@ -150,12 +84,12 @@ - + - + - + @@ -248,102 +182,188 @@ - + - - - - - - + + - - + + - - + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + + + + + - + - + - + - + - + - + @@ -354,49 +374,62 @@ - + - + + - + + + + + + - - - - + + + + + - - - + + + + + + + - + + + - + - + - + - + - + - + @@ -407,58 +440,55 @@ - + - - - - + - - - + - + - - - - - + + + + - - - - + + + + - - - - - + + + + - - - - - + + + + - - - - + + + + + + + + + - - - + + + @@ -471,38 +501,38 @@ - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + @@ -511,26 +541,11 @@ - - + + - - - - - - - - - - - - - - - @@ -541,29 +556,14 @@ - - - - - - - - - - - - - - - - - - + + + - - - + + + diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh index e57c191..07f27d1 100755 --- a/examples/generate_ecss_demo.sh +++ b/examples/generate_ecss_demo.sh @@ -4,3 +4,5 @@ template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfa pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_6_requirement_traceability.pdf output/ecss-e-st-40c_6_requirement_traceability.md template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_4_aspects_of_each_component.pdf output/ecss-e-st-40c_5_4_aspects_of_each_component.md +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_2_software_dynamic_architecture.pdf output/ecss-e-st-40c_4_2_software_dynamic_architecture.md diff --git a/templateprocessor/iv.py b/templateprocessor/iv.py index 8be8a4e..0bffeb5 100644 --- a/templateprocessor/iv.py +++ b/templateprocessor/iv.py @@ -78,6 +78,7 @@ class FunctionInterface: id: str name: str + comment: str kind: InterfaceKind enable_multicast: bool = True layer: str = "default" diff --git a/templateprocessor/ivreader.py b/templateprocessor/ivreader.py index 5d4efc7..58509de 100644 --- a/templateprocessor/ivreader.py +++ b/templateprocessor/ivreader.py @@ -177,6 +177,7 @@ def _parse_interface(self, elem: ET.Element) -> FunctionInterface: iface = FunctionInterface( id=elem.get("id", ""), name=elem.get("name", ""), + comment=elem.get("Comment", ""), kind=InterfaceKind(elem.get("kind", "")), enable_multicast=elem.get("enable_multicast", "true") == "true", layer=elem.get("layer", "default"), From ec4e51a20bdf674064685dfdb44d420e315b4640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Wed, 10 Dec 2025 19:09:39 +0100 Subject: [PATCH 18/28] Added initial version of internal_inferfaces_design template --- ...st-40c_5_5_internal_interface_design.tmplt | 158 +++++++++++++++++- examples/generate_ecss_demo.sh | 2 + 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt index e89bed5..5aa3ab9 100644 --- a/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt +++ b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt @@ -1 +1,157 @@ -TODO internal interfaces, including data \ No newline at end of file +<% +## Data Initialization +# Get all functions + +def find_by_name(all, name): + for f in all: + if f.name == name: + return f + return None + +funcs = [] +def get_function_children(func): + result = [] + if func.nested_functions: + for nested in func.nested_functions: + result.append(nested) + result.extend(get_function_children(nested)) + return result + +for func in interface_view.functions: + funcs.append(func) + funcs.extend(get_function_children(func)) + +funcs.sort(key=lambda f: f.name.lower()) + +# Get functions deployed to the target partition +target_partition_name = values["TARGET"] + +deployed_funcs = [] +target_partition = None +for node in deployment_view.nodes: + for partition in node.partitions: + if partition.name == target_partition_name: + target_partition = partition + +deployed_func_names = [f.name for f in target_partition.functions] +for fun in funcs: + if fun.name in deployed_func_names: + deployed_funcs.append(fun) + + + +# Find and crossreference all connections +def get_function_connections(func): + result = [] + if func.nested_connections: + result.extend(func.nested_connections) + if func.nested_functions: + for nested in func.nested_functions: + result.extend(get_function_connections(nested)) + return result + +connections = [] +connections.extend(interface_view.connections) +for func in interface_view.functions: + connections.extend(get_function_connections(func)) + +internal_connections = [] + +for connection in connections: + if connection.source is None or connection.source.function_name is None: + continue + if connection.target is None or connection.target.function_name is None: + continue + if connection.target.function_name in deployed_func_names and connection.source.function_name in deployed_func_names: + internal_connections.append(connection) + +iface_source_map = {} +iface_target_map = {} + +for connection in internal_connections: + meta = {} + meta["connection"] = connection + meta["source_function"] = find_by_name(funcs, connection.source.function_name) + if meta["source_function"] is None: + print(f"Source function {connection.source.function_name} not found") + continue + meta["source_iface"] = find_by_name(meta["source_function"].provided_interfaces + meta["source_function"].required_interfaces, connection.source.iface_name) + meta["target_function"] = find_by_name(funcs, connection.target.function_name) + if meta["target_function"] is None: + print(f"Target function {connection.target.function_name} not found") + continue + meta["target_iface"] = find_by_name(meta["target_function"].provided_interfaces + meta["target_function"].required_interfaces, connection.source.iface_name) + source_handle = f"{meta["source_function"].name}__{meta["source_iface"].name}" + target_handle = f"{meta["target_function"].name}__{meta["target_iface"].name}" + if not source_handle in iface_source_map.keys(): + iface_source_map[source_handle] = [] + iface_source_map[source_handle].append((meta["target_function"], meta["target_iface"])) + if not target_handle in iface_target_map.keys(): + iface_target_map[target_handle] = [] + iface_target_map[target_handle].append((meta["source_function"], meta["source_iface"])) + +%> + +The list below summarizes all internal interfaces between user components. + +% for func in deployed_funcs: +% for iface in func.provided_interfaces + func.required_interfaces: +<% +iface_handle = f"{func.name}__{iface.name}" +if not iface_handle in iface_source_map and not iface_handle in iface_target_map: + continue +%> \ +${"##"} \ +% if iface in func.provided_interfaces: +[PROVIDED] \ +% else: +[REQUIRED] \ +% endif +[${iface.kind.value}] ${func.name}::${iface.name} + +- Description: ${iface.comment} + +% if iface.kind.value != "Cyclic": +- Parameters: +% for param in iface.input_parameters + iface.output_parameters: + + - \ +% if param in iface.input_parameters: +[IN] \ +% else: +[OUT] \ +% endif +${param.name} ${param.type} (with ${param.encoding.value} encoding) +% endfor +% endif + +% if iface_handle in iface_source_map: +- Connects to: +% for (other_function, other_iface) in iface_source_map[iface_handle]: + + - ${other_function.name}::${other_iface.name} +% endfor +% endif + +% if iface_handle in iface_target_map: +- Is connected from: +% for (other_function, other_iface) in iface_target_map[iface_handle]: + + - ${other_function.name}::${other_iface.name} +% endfor +% endif + +% endfor +% endfor + +The list below summarizes all internal connections between the interfaces: +% for connection in internal_connections: +<% + if connection.source is None or connection.source.function_name is None: + continue + if connection.target is None or connection.target.function_name is None: + continue +%> \ + +- Connection from ${connection.source.function_name}::${connection.source.iface_name} to ${connection.target.function_name}::${connection.target.iface_name} +% endfor \ No newline at end of file diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh index 07f27d1..633c5e9 100755 --- a/examples/generate_ecss_demo.sh +++ b/examples/generate_ecss_demo.sh @@ -6,3 +6,5 @@ template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfa pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_4_aspects_of_each_component.pdf output/ecss-e-st-40c_5_4_aspects_of_each_component.md template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_2_software_dynamic_architecture.pdf output/ecss-e-st-40c_4_2_software_dynamic_architecture.md +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_5_internal_interface_design.pdf output/ecss-e-st-40c_5_5_internal_interface_design.md \ No newline at end of file From 01405113367179c895d29e3f6162facc1c1c3932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Wed, 10 Dec 2025 19:13:40 +0100 Subject: [PATCH 19/28] Fixed existing tests --- tests/test_templateinstantiator.py | 36 +++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/tests/test_templateinstantiator.py b/tests/test_templateinstantiator.py index b82d0ec..85b96d0 100644 --- a/tests/test_templateinstantiator.py +++ b/tests/test_templateinstantiator.py @@ -45,13 +45,18 @@ def create_sample_interface_view() -> InterfaceView: # Create a sample function func = Function( - id="func_1", name="TestFunction", is_type=False, language=Language.C + id="func_1", + comment="No Comment", + name="TestFunction", + is_type=False, + language=Language.C, ) # Add a provided interface pi = ProvidedInterface( id="pi_1", name="test_pi", + comment="No Comment", kind=InterfaceKind.SPORADIC, ) pi.input_parameters = [ @@ -63,6 +68,7 @@ def create_sample_interface_view() -> InterfaceView: ri = RequiredInterface( id="ri_1", name="test_ri", + comment="No Comment", kind=InterfaceKind.CYCLIC, ) ri.output_parameters = [ @@ -214,7 +220,7 @@ def test_instantiator_initialization(self): dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) assert instantiator.interface_view == iv assert instantiator.system_object_types == so_types @@ -225,7 +231,7 @@ def test_instantiate_simple_template(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = "Hello World!" @@ -239,7 +245,7 @@ def test_instantiate_template_with_interface_view(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """Interface View version: ${interface_view.version} ASN1 file: ${interface_view.asn1file} @@ -257,7 +263,7 @@ def test_instantiate_template_with_function_details(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """% for func in interface_view.functions: Function: ${func.name} @@ -279,7 +285,7 @@ def test_instantiate_template_with_system_object_types(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """Number of System Object Types: ${len(system_object_types)} % for name, so_type in system_object_types.items(): @@ -300,7 +306,7 @@ def test_instantiate_template_with_system_object_instances(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """% for name, so_type in system_object_types.items(): [${name}] @@ -331,7 +337,7 @@ def test_instantiate_template_with_empty_data(self): so_types = {} - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """Version: ${interface_view.version} Functions: ${len(interface_view.functions)} @@ -349,7 +355,7 @@ def test_instantiate_template_with_python_expressions(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """<% total_interfaces = sum(len(f.provided_interfaces) + len(f.required_interfaces) for f in interface_view.functions) @@ -369,7 +375,7 @@ def test_instantiate_template_with_deployment_view(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """Deployment View version: ${deployment_view.version} UI file: ${deployment_view.ui_file} @@ -389,7 +395,7 @@ def test_instantiate_template_with_deployment_nodes(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """% for node in deployment_view.nodes: Node: ${node.name} @@ -415,7 +421,7 @@ def test_instantiate_template_with_partitions_and_functions(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """% for node in deployment_view.nodes: ${node.name} @@ -445,7 +451,7 @@ def test_instantiate_template_with_devices(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """% for node in deployment_view.nodes: % if node.devices: @@ -473,7 +479,7 @@ def test_instantiate_template_with_connections(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """% for conn in deployment_view.connections: Connection: ${conn.name} @@ -503,7 +509,7 @@ def test_instantiate_template_with_node_requirements(self): iv = self.create_sample_interface_view() dv = self.create_sample_deployment_view() so_types = self.create_sample_system_object_types() - instantiator = TemplateInstantiator(iv, dv, so_types) + instantiator = TemplateInstantiator(iv, dv, so_types, {}) template = """% for node in deployment_view.nodes: Node: ${node.name} From f5c99e3bb520cb94d4eee1f840d085e6fb6f1b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Wed, 10 Dec 2025 19:32:21 +0100 Subject: [PATCH 20/28] Added test for value instantiation --- tests/test_templateinstantiator.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_templateinstantiator.py b/tests/test_templateinstantiator.py index 85b96d0..040a500 100644 --- a/tests/test_templateinstantiator.py +++ b/tests/test_templateinstantiator.py @@ -527,3 +527,31 @@ def test_instantiate_template_with_node_requirements(self): assert "Requirements: r20, r21" in result assert "Node: SAM V71 RTEMS N7S_1" in result assert "Requirements: None" in result + + def test_instantiate_template_with_values(self): + """Test instantiating a template that uses provided values.""" + iv = self.create_sample_interface_view() + dv = self.create_sample_deployment_view() + so_types = self.create_sample_system_object_types() + + values = { + "project_name": "MyProject", + "version": "2.1.0", + "author": "Test Author", + "description": "Test project description", + } + + instantiator = TemplateInstantiator(iv, dv, so_types, values) + + template = """Project: ${values['project_name']} +Version: ${values['version']} +Author: ${values['author']} +Description: ${values['description']}""" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert "Project: MyProject" in result + assert "Version: 2.1.0" in result + assert "Author: Test Author" in result + assert "Description: Test project description" in result From 16f2b14407526a5138e68b5f15957b51f6a7e66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Wed, 10 Dec 2025 21:18:55 +0100 Subject: [PATCH 21/28] Fixed filtering of container functions --- ...s-e-st-40c_5_5_internal_interface_design.tmplt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt index 5aa3ab9..9a76a6b 100644 --- a/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt +++ b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt @@ -38,6 +38,15 @@ for fun in funcs: if fun.name in deployed_func_names: deployed_funcs.append(fun) +# Only leaf functions are deployed, so a correction for parents must be applied +for func in funcs: + if func.nested_functions: + for nested in func.nested_functions: + if nested in deployed_funcs and not func in deployed_funcs: + deployed_funcs.append(func) + deployed_func_names.append(func.name) + +print(f"Deployed Functions: {[func.name for func in deployed_funcs]}") # Find and crossreference all connections @@ -58,12 +67,18 @@ for func in interface_view.functions: internal_connections = [] for connection in connections: + print(f"Processing connection {connection.name} from {connection.source.function_name} to {connection.target.function_name}") if connection.source is None or connection.source.function_name is None: + print(f" -Rejecting due to SOURCE") continue if connection.target is None or connection.target.function_name is None: + print(f" -Rejecting due to TARGET") continue if connection.target.function_name in deployed_func_names and connection.source.function_name in deployed_func_names: + print(f" +Adding as INTERNAL") internal_connections.append(connection) + else: + print(f" -Rejecting as EXTERNAL") iface_source_map = {} iface_target_map = {} From 83929d80de702de3b263eab29b542d93239f4d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Thu, 11 Dec 2025 01:09:47 +0100 Subject: [PATCH 22/28] Added interfaces context template --- ...ecss-e-st-40c_4_4_interfaces_context.tmplt | 173 ++++++++++++++++++ ...st-40c_5_5_internal_interface_design.tmplt | 5 +- examples/generate_ecss_demo.sh | 4 +- 3 files changed, 179 insertions(+), 3 deletions(-) diff --git a/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt b/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt index e69de29..193dc64 100644 --- a/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt +++ b/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt @@ -0,0 +1,173 @@ +<% +## Data Initialization +# Get all functions + +def find_by_name(all, name): + for f in all: + if f.name == name: + return f + return None + +funcs = [] +def get_function_children(func): + result = [] + if func.nested_functions: + for nested in func.nested_functions: + result.append(nested) + result.extend(get_function_children(nested)) + return result + +for func in interface_view.functions: + funcs.append(func) + funcs.extend(get_function_children(func)) + +funcs.sort(key=lambda f: f.name.lower()) + +# Get functions deployed to the target partition +target_partition_name = values["TARGET"] + +deployed_funcs = [] +target_partition = None +for node in deployment_view.nodes: + for partition in node.partitions: + if partition.name == target_partition_name: + target_partition = partition + +deployed_func_names = [f.name for f in target_partition.functions] +for fun in funcs: + if fun.name in deployed_func_names: + deployed_funcs.append(fun) + +# Only leaf functions are deployed, so a correction for parents must be applied +for func in funcs: + if func.nested_functions: + for nested in func.nested_functions: + if nested in deployed_funcs and not func in deployed_funcs: + deployed_funcs.append(func) + deployed_func_names.append(func.name) + +print(f"Deployed Functions: {[func.name for func in deployed_funcs]}") + +# Find and crossreference all connections +def get_function_connections(func): + result = [] + if func.nested_connections: + result.extend(func.nested_connections) + if func.nested_functions: + for nested in func.nested_functions: + result.extend(get_function_connections(nested)) + return result + +connections = [] +connections.extend(interface_view.connections) +for func in interface_view.functions: + connections.extend(get_function_connections(func)) + +external_connections = [] + +for connection in connections: + print(f"Processing connection {connection.name} from {connection.source.function_name} to {connection.target.function_name}") + if connection.source is None or connection.source.function_name is None: + print(f" -Rejecting due to SOURCE") + continue + if connection.target is None or connection.target.function_name is None: + print(f" -Rejecting due to TARGET") + continue + target_inside = connection.target.function_name in deployed_func_names + source_inside = connection.source.function_name in deployed_func_names + if (target_inside and not source_inside) or (not target_inside and source_inside): + print(f" +Adding as BOUNDARY") + external_connections.append(connection) + else: + print(f" -Rejecting as NON-BOUNDARY") + +iface_source_map = {} +iface_target_map = {} + +for connection in external_connections: + meta = {} + meta["connection"] = connection + meta["source_function"] = find_by_name(funcs, connection.source.function_name) + if meta["source_function"] is None: + print(f"Source function {connection.source.function_name} not found") + continue + meta["source_iface"] = find_by_name(meta["source_function"].provided_interfaces + meta["source_function"].required_interfaces, connection.source.iface_name) + meta["target_function"] = find_by_name(funcs, connection.target.function_name) + if meta["target_function"] is None: + print(f"Target function {connection.target.function_name} not found") + continue + meta["target_iface"] = find_by_name(meta["target_function"].provided_interfaces + meta["target_function"].required_interfaces, connection.source.iface_name) + source_handle = f"{meta["source_function"].name}__{meta["source_iface"].name}" + target_handle = f"{meta["target_function"].name}__{meta["target_iface"].name}" + if not source_handle in iface_source_map.keys(): + iface_source_map[source_handle] = [] + iface_source_map[source_handle].append((meta["target_function"], meta["target_iface"])) + if not target_handle in iface_target_map.keys(): + iface_target_map[target_handle] = [] + iface_target_map[target_handle].append((meta["source_function"], meta["source_iface"])) + +%> + +The list below summarizes all external interfaces. + +% for func in deployed_funcs: +% for iface in func.provided_interfaces + func.required_interfaces: +<% +iface_handle = f"{func.name}__{iface.name}" +if not iface_handle in iface_source_map and not iface_handle in iface_target_map: + continue +%> +${"#"} \ +% if iface in func.provided_interfaces: +[PROVIDED] \ +% else: +[REQUIRED] \ +% endif +[${iface.kind.value}] ${func.name}::${iface.name} + +- Description: ${iface.comment} + +% if iface.kind.value != "Cyclic": +- Parameters: +% for param in iface.input_parameters + iface.output_parameters: + + - \ +% if param in iface.input_parameters: +[IN] \ +% else: +[OUT] \ +% endif +${param.name} ${param.type} (with ${param.encoding.value} encoding) +% endfor +% endif + +% if iface_handle in iface_source_map: +- Connects to: +% for (other_function, other_iface) in iface_source_map[iface_handle]: + + - ${other_function.name}::${other_iface.name} +% endfor +% endif + +% if iface_handle in iface_target_map: +- Is connected from: +% for (other_function, other_iface) in iface_target_map[iface_handle]: + + - ${other_function.name}::${other_iface.name} +% endfor +% endif + +% endfor +% endfor + +The list below summarizes all external connections: +% for connection in external_connections: +<% + if connection.source is None or connection.source.function_name is None: + continue + if connection.target is None or connection.target.function_name is None: + continue +%> \ + +- Connection from ${connection.source.function_name}::${connection.source.iface_name} to ${connection.target.function_name}::${connection.target.iface_name} +% endfor \ No newline at end of file diff --git a/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt index 9a76a6b..3549234 100644 --- a/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt +++ b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt @@ -115,8 +115,9 @@ The list below summarizes all internal interfaces between user components. iface_handle = f"{func.name}__{iface.name}" if not iface_handle in iface_source_map and not iface_handle in iface_target_map: continue -%> \ -${"##"} \ +%> + +${"#"} \ % if iface in func.provided_interfaces: [PROVIDED] \ % else: diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh index 633c5e9..efba071 100755 --- a/examples/generate_ecss_demo.sh +++ b/examples/generate_ecss_demo.sh @@ -7,4 +7,6 @@ pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_4_aspects_of_each_co template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_2_software_dynamic_architecture.pdf output/ecss-e-st-40c_4_2_software_dynamic_architecture.md template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt -pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_5_internal_interface_design.pdf output/ecss-e-st-40c_5_5_internal_interface_design.md \ No newline at end of file +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_5_internal_interface_design.pdf output/ecss-e-st-40c_5_5_internal_interface_design.md +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_4_interfaces_context.pdf output/ecss-e-st-40c_4_4_interfaces_context.md \ No newline at end of file From 2504eac350508f737ba1c72640d97c1c34af0e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Thu, 11 Dec 2025 01:35:22 +0100 Subject: [PATCH 23/28] Added static architecture template --- ...40c_4_1_software_static_architecture.tmplt | 83 ++++++++++++++++++- ...ecss-e-st-40c_4_4_interfaces_context.tmplt | 8 -- ...st-40c_5_4_aspects_of_each_component.tmplt | 8 ++ ...st-40c_5_5_internal_interface_design.tmplt | 7 -- examples/generate_ecss_demo.sh | 4 +- 5 files changed, 93 insertions(+), 17 deletions(-) diff --git a/data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt b/data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt index 1ec43e0..a8274c2 100644 --- a/data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt +++ b/data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt @@ -1 +1,82 @@ -TODO architecture of the software item, including relationship between its major components \ No newline at end of file +<% +## Data Initialization +# Get all functions + +funcs = [] +def get_function_children(func): + result = [] + if func.nested_functions: + for nested in func.nested_functions: + result.append(nested) + result.extend(get_function_children(nested)) + return result + +for func in interface_view.functions: + funcs.append(func) + funcs.extend(get_function_children(func)) + +funcs.sort(key=lambda f: f.name.lower()) + +# Get functions deployed to the target partition +target_partition_name = values["TARGET"] + +deployed_funcs = [] +target_partition = None +target_node = None +for node in deployment_view.nodes: + for partition in node.partitions: + if partition.name == target_partition_name: + target_node = node + target_partition = partition + +deployed_func_names = [f.name for f in target_partition.functions] +for fun in funcs: + if fun.name in deployed_func_names: + deployed_funcs.append(fun) + +# Only leaf functions are deployed, so a correction for parents must be applied +for func in funcs: + if func.nested_functions: + for nested in func.nested_functions: + if nested in deployed_funcs and not func in deployed_funcs: + deployed_funcs.append(func) + deployed_func_names.append(func.name) + +# Get used implementations +languages = set() +for func in deployed_funcs: + languages.add(func.language) + +%> + +The software architecture of ${target_partition_name} consists of ${len(deployed_funcs)} functions deployed onto ${target_node.name} node. +The functions use the following implementation technologies: ${",".join([l.value for l in languages])}. +The top-level components are as follows: +% for func in interface_view.functions: +<% +if not func.name in deployed_func_names: + continue +is_composite = func.nested_functions and len(func.nested_functions) > 0 +implementation_text = "[COMPOSITE] " if is_composite else func.language.value +%> + +- ${func.name} [${implementation_text}] - ${func.comment} +% endfor +## Print the level 2 functions +% for func in interface_view.functions: +<% +if not func.nested_functions or len(func.nested_functions) == 0: + continue +%> + +Function ${func.name} is a composite, containing the following sub-functions: +% for nested in func.nested_functions: +<% +is_composite = nested.nested_functions and len(nested.nested_functions) > 0 +implementation_text = "[COMPOSITE] " if is_composite else nested.language.value +%> + +- ${nested.name} [${implementation_text}] - ${nested.comment} +% endfor + +% endfor \ No newline at end of file diff --git a/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt b/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt index 193dc64..bfc8968 100644 --- a/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt +++ b/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt @@ -46,8 +46,6 @@ for func in funcs: deployed_funcs.append(func) deployed_func_names.append(func.name) -print(f"Deployed Functions: {[func.name for func in deployed_funcs]}") - # Find and crossreference all connections def get_function_connections(func): result = [] @@ -66,20 +64,14 @@ for func in interface_view.functions: external_connections = [] for connection in connections: - print(f"Processing connection {connection.name} from {connection.source.function_name} to {connection.target.function_name}") if connection.source is None or connection.source.function_name is None: - print(f" -Rejecting due to SOURCE") continue if connection.target is None or connection.target.function_name is None: - print(f" -Rejecting due to TARGET") continue target_inside = connection.target.function_name in deployed_func_names source_inside = connection.source.function_name in deployed_func_names if (target_inside and not source_inside) or (not target_inside and source_inside): - print(f" +Adding as BOUNDARY") external_connections.append(connection) - else: - print(f" -Rejecting as NON-BOUNDARY") iface_source_map = {} iface_target_map = {} diff --git a/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt b/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt index 46ce4fb..d878524 100644 --- a/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt +++ b/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt @@ -32,6 +32,14 @@ for fun in funcs: if fun.name in deployed_func_names: deployed_funcs.append(fun) +# Only leaf functions are deployed, so a correction for parents must be applied +for func in funcs: + if func.nested_functions: + for nested in func.nested_functions: + if nested in deployed_funcs and not func in deployed_funcs: + deployed_funcs.append(func) + deployed_func_names.append(func.name) + %> The below chapters summarize aspects of each component. diff --git a/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt index 3549234..dd2ecac 100644 --- a/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt +++ b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt @@ -46,7 +46,6 @@ for func in funcs: deployed_funcs.append(func) deployed_func_names.append(func.name) -print(f"Deployed Functions: {[func.name for func in deployed_funcs]}") # Find and crossreference all connections @@ -67,18 +66,12 @@ for func in interface_view.functions: internal_connections = [] for connection in connections: - print(f"Processing connection {connection.name} from {connection.source.function_name} to {connection.target.function_name}") if connection.source is None or connection.source.function_name is None: - print(f" -Rejecting due to SOURCE") continue if connection.target is None or connection.target.function_name is None: - print(f" -Rejecting due to TARGET") continue if connection.target.function_name in deployed_func_names and connection.source.function_name in deployed_func_names: - print(f" +Adding as INTERNAL") internal_connections.append(connection) - else: - print(f" -Rejecting as EXTERNAL") iface_source_map = {} iface_target_map = {} diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh index efba071..8ce16b9 100755 --- a/examples/generate_ecss_demo.sh +++ b/examples/generate_ecss_demo.sh @@ -9,4 +9,6 @@ pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_2_software_dynamic_a template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_5_internal_interface_design.pdf output/ecss-e-st-40c_5_5_internal_interface_design.md template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt -pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_4_interfaces_context.pdf output/ecss-e-st-40c_4_4_interfaces_context.md \ No newline at end of file +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_4_interfaces_context.pdf output/ecss-e-st-40c_4_4_interfaces_context.md +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_1_software_static_architecture.pdf output/ecss-e-st-40c_4_1_software_static_architecture.md \ No newline at end of file From a8ebd195e4daaeead3ef2f8651f18bd64c22918f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Thu, 11 Dec 2025 02:19:55 +0100 Subject: [PATCH 24/28] Added overall architecture template --- .../ecss-e-st-40c_5_1_software_design.tmplt | 0 ...ss-e-st-40c_5_2_overall_architecture.tmplt | 102 ++++++++++++++++++ ...-40c_5_3_software_copmponents_design.tmplt | 51 +++++++++ examples/generate_ecss_demo.sh | 20 ++-- 4 files changed, 165 insertions(+), 8 deletions(-) delete mode 100644 data/ecss-template/ecss-e-st-40c_5_1_software_design.tmplt diff --git a/data/ecss-template/ecss-e-st-40c_5_1_software_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_1_software_design.tmplt deleted file mode 100644 index e69de29..0000000 diff --git a/data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt b/data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt index e69de29..7ad8a05 100644 --- a/data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt +++ b/data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt @@ -0,0 +1,102 @@ +<% +## Data Initialization +# Get all functions + +funcs = [] +def get_function_children(func): + result = [] + if func.nested_functions: + for nested in func.nested_functions: + result.append(nested) + result.extend(get_function_children(nested)) + return result + +for func in interface_view.functions: + funcs.append(func) + funcs.extend(get_function_children(func)) + +funcs.sort(key=lambda f: f.name.lower()) + +# Get functions deployed to the target partition +target_partition_name = values["TARGET"] + +deployed_funcs = [] +target_partition = None +target_node = None +for node in deployment_view.nodes: + for partition in node.partitions: + if partition.name == target_partition_name: + target_node = node + target_partition = partition + +deployed_func_names = [f.name for f in target_partition.functions] +for fun in funcs: + if fun.name in deployed_func_names: + deployed_funcs.append(fun) + +# Only leaf functions are deployed, so a correction for parents must be applied +for func in funcs: + if func.nested_functions: + for nested in func.nested_functions: + if nested in deployed_funcs and not func in deployed_funcs: + deployed_funcs.append(func) + deployed_func_names.append(func.name) + +%> + +Components of ${target_partition_name} are enumerated in 4.1 Software Static Architecture, while threads are de described in 4.2 Software Dynamic Architecture. +Details of each of the components are provided in 5.4 Aspects of each Component. Scheduling is pre-emptive, multithreaded, provided by ${node.type}. +<% +mutex_function_names = set() +thread_function_names = set() +for func in deployed_funcs: + if len(func.nested_functions) > 0: + continue + for pi in func.provided_interfaces: + if pi.kind.value != "Unprotected": + mutex_function_names.add(func.name) + if pi.kind.value == "Cyclic" or pi.kind.value == "Sporadic": + thread_function_names.add(func.name) +%> +The following functions host threads for cyclic or sporadic interfaces (described in Chapter 4.2): +% for func in deployed_funcs: +<% +if not func.name in thread_function_names: + continue +%> + +- ${func.name} +% endfor + +The following functions host semaphores, due to providing at least once cyclic, sporadic or protected interface: +% for func in deployed_funcs: +<% +if not func.name in mutex_function_names: + continue +%> + +- ${func.name} +% endfor + +Table below lists the queues that the components interact through. Connections are described in detail in Chapter 5.5. + +| Queue | Item Type | Size | +| - | - | - | +% for func in deployed_funcs: +<% +if len(func.nested_functions) > 0: + continue +%>\ +% for pi in func.provided_interfaces: +<% +if pi.kind.value != "Sporadic": + continue + +param = "Dummy Item" +if len(pi.input_parameters) > 0: + param = pi.input_parameters[0].type +%>\ +| ${func.name}::${pi.name} | ${param} | ${pi.queue_size} | +% endfor +% endfor + diff --git a/data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt index e69de29..467b979 100644 --- a/data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt +++ b/data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt @@ -0,0 +1,51 @@ +<% +## Data Initialization +# Get all functions + +funcs = [] +def get_function_children(func): + result = [] + if func.nested_functions: + for nested in func.nested_functions: + result.append(nested) + result.extend(get_function_children(nested)) + return result + +for func in interface_view.functions: + funcs.append(func) + funcs.extend(get_function_children(func)) + +funcs.sort(key=lambda f: f.name.lower()) + +# Get functions deployed to the target partition +target_partition_name = values["TARGET"] + +deployed_funcs = [] +target_partition = None +for node in deployment_view.nodes: + for partition in node.partitions: + if partition.name == target_partition_name: + target_partition = partition + +deployed_func_names = [f.name for f in target_partition.functions] +for fun in funcs: + if fun.name in deployed_func_names: + deployed_funcs.append(fun) + +# Only leaf functions are deployed, so a correction for parents must be applied +for func in funcs: + if func.nested_functions: + for nested in func.nested_functions: + if nested in deployed_funcs and not func in deployed_funcs: + deployed_funcs.append(func) + deployed_func_names.append(func.name) + +%> + +The below table lists all components of ${target_partition_name}. + +| Function | Type | Description | +| -| - | - | +% for func in deployed_funcs: +| ${func.name} | ${func.language.value if len(func.nested_functions) == 0 else "COMPOSITE" } | ${func.comment} | +% endfor \ No newline at end of file diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh index 8ce16b9..0d33eee 100755 --- a/examples/generate_ecss_demo.sh +++ b/examples/generate_ecss_demo.sh @@ -1,14 +1,18 @@ #!/bin/bash mkdir -p output -template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt -pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_6_requirement_traceability.pdf output/ecss-e-st-40c_6_requirement_traceability.md -template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt -pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_4_aspects_of_each_component.pdf output/ecss-e-st-40c_5_4_aspects_of_each_component.md +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_1_software_static_architecture.pdf output/ecss-e-st-40c_4_1_software_static_architecture.md template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_2_software_dynamic_architecture.pdf output/ecss-e-st-40c_4_2_software_dynamic_architecture.md -template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt -pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_5_internal_interface_design.pdf output/ecss-e-st-40c_5_5_internal_interface_design.md template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_4_interfaces_context.pdf output/ecss-e-st-40c_4_4_interfaces_context.md -template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt -pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_1_software_static_architecture.pdf output/ecss-e-st-40c_4_1_software_static_architecture.md \ No newline at end of file +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_2_overall_architecture.pdf output/ecss-e-st-40c_5_2_overall_architecture.md +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_3_software_copmponents_design.pdf output/ecss-e-st-40c_5_3_software_copmponents_design.md +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_4_aspects_of_each_component.pdf output/ecss-e-st-40c_5_4_aspects_of_each_component.md +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_5_internal_interface_design.pdf output/ecss-e-st-40c_5_5_internal_interface_design.md +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_6_requirement_traceability.pdf output/ecss-e-st-40c_6_requirement_traceability.md \ No newline at end of file From f2c01bd9136acd863493659850e916614791c717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Thu, 11 Dec 2025 02:25:57 +0100 Subject: [PATCH 25/28] Improved System Objects example --- examples/generate_so_list.sh | 3 ++- examples/so_list.tmplt | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/generate_so_list.sh b/examples/generate_so_list.sh index 298f8bb..9fef728 100755 --- a/examples/generate_so_list.sh +++ b/examples/generate_so_list.sh @@ -1,3 +1,4 @@ #!/bin/bash mkdir -p output -template-processor --verbosity info --sos ../data/events.csv -o output -t so_list.tmplt \ No newline at end of file +template-processor --verbosity info --sos ../data/events.csv -o output -t so_list.tmplt +pandoc --pdf-engine=pdfroff --output=output/so_list.pdf output/so_list.md \ No newline at end of file diff --git a/examples/so_list.tmplt b/examples/so_list.tmplt index 2260540..e80082c 100644 --- a/examples/so_list.tmplt +++ b/examples/so_list.tmplt @@ -1,8 +1,10 @@ ### List of all System Objects % for name, so_type in system_object_types.items(): -[${name}] + +# [${name}] Properties: ${', '.join(so_type.property_names)} % for idx, instance in enumerate(so_type.instances): -Instance ${idx}: ${', '.join(instance.values.values())} + +- Instance ${idx}: ${', '.join(instance.values.values())} % endfor % endfor \ No newline at end of file From 82b67308b390faea9386dea4602c752c4e9e104c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Thu, 11 Dec 2025 12:51:51 +0100 Subject: [PATCH 26/28] Review fixes --- ...st-40c_5_3_software_components_design.tmplt} | 0 examples/generate_ecss_demo.sh | 4 ++-- templateprocessor/cli.py | 17 +++++++++++++---- templateprocessor/ivreader.py | 8 ++++++-- templateprocessor/templateinstantiator.py | 8 ++++---- tests/test_templateinstantiator.py | 3 +-- 6 files changed, 26 insertions(+), 14 deletions(-) rename data/ecss-template/{ecss-e-st-40c_5_3_software_copmponents_design.tmplt => ecss-e-st-40c_5_3_software_components_design.tmplt} (100%) diff --git a/data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_3_software_components_design.tmplt similarity index 100% rename from data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt rename to data/ecss-template/ecss-e-st-40c_5_3_software_components_design.tmplt diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh index 0d33eee..84ce76a 100755 --- a/examples/generate_ecss_demo.sh +++ b/examples/generate_ecss_demo.sh @@ -8,8 +8,8 @@ template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfa pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_4_interfaces_context.pdf output/ecss-e-st-40c_4_4_interfaces_context.md template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_2_overall_architecture.pdf output/ecss-e-st-40c_5_2_overall_architecture.md -template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_3_software_copmponents_design.tmplt -pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_3_software_copmponents_design.pdf output/ecss-e-st-40c_5_3_software_copmponents_design.md +template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_3_software_components_design.tmplt +pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_3_software_components_design.pdf output/ecss-e-st-40c_5_3_software_components_design.md template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_4_aspects_of_each_component.pdf output/ecss-e-st-40c_5_4_aspects_of_each_component.md template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt diff --git a/templateprocessor/cli.py b/templateprocessor/cli.py index ba8dadd..edf57a0 100644 --- a/templateprocessor/cli.py +++ b/templateprocessor/cli.py @@ -4,7 +4,6 @@ import logging import argparse -import os from pathlib import Path import sys from templateprocessor import __version__ @@ -63,6 +62,13 @@ def parse_arguments() -> argparse.Namespace: action="append", ) + parser.add_argument( + "-m", + "--module-directory", + help="Module directory for Mako templating engine", + metavar="FILE", + ) + parser.add_argument( "-o", "--output", @@ -106,14 +112,14 @@ def get_values_dictionary(values: list[str]) -> dict[str, str]: result = {} for pair in values: if pair.count("=") != 1: - raise Exception( + raise ValueError( f"Pair [{pair}] contains incorrect number of name/value separators (=)" ) split = pair.split("=") name = split[0].strip() value = split[1].strip() if len(name) == 0: - raise Exception(f"Name in [{pair}] is empty") + raise ValueError(f"Name in [{pair}] is empty") # value can be empty result[name] = value return result @@ -133,6 +139,7 @@ def main(): logging.debug(f"Values: {args.value}") logging.debug(f"Templates: {args.template}") logging.debug(f"Output Directory: {args.output}") + logging.debug(f"Module directory: {args.module_directory}") logging.info(f"Reading Interface View from {args.iv}") iv = IVReader().read(args.iv) if args.iv else InterfaceView() @@ -162,7 +169,9 @@ def main(): with open(template_file, "r") as f: template = f.read() logging.debug(f"Instantiating template:\n {template}") - instantiated_template = instantiator.instantiate(template, "") + instantiated_template = instantiator.instantiate( + template, args.module_directory + ) logging.debug(f"Instantiation:\n {instantiated_template}") output = Path(args.output) / f"{name}.md" logging.debug(f"Saving to {output}") diff --git a/templateprocessor/ivreader.py b/templateprocessor/ivreader.py index 58509de..293abed 100644 --- a/templateprocessor/ivreader.py +++ b/templateprocessor/ivreader.py @@ -135,7 +135,9 @@ def _parse_function(self, elem: ET.Element) -> Function: if elem.get("type_language") else None ), - requirement_ids=elem.get("requirement_ids", "").split(","), + requirement_ids=[ + rid for rid in elem.get("requirement_ids", "").split(",") if rid + ], ) # Parse properties @@ -195,7 +197,9 @@ def _parse_interface(self, elem: ET.Element) -> FunctionInterface: else None ), priority=int(elem.get("priority")) if elem.get("priority") else None, - requirement_ids=elem.get("requirement_ids", "").split(","), + requirement_ids=[ + rid for rid in elem.get("requirement_ids", "").split(",") if rid + ], ) # Parse input parameters diff --git a/templateprocessor/templateinstantiator.py b/templateprocessor/templateinstantiator.py index d2a32b2..25f0c62 100644 --- a/templateprocessor/templateinstantiator.py +++ b/templateprocessor/templateinstantiator.py @@ -6,8 +6,8 @@ from templateprocessor.iv import InterfaceView from templateprocessor.dv import DeploymentView -from templateprocessor.so import SystemObjectType, SystemObject -from typing import List, Dict +from templateprocessor.so import SystemObjectType +from typing import Dict from mako.template import Template @@ -16,8 +16,8 @@ class TemplateInstantiator: Instantiator of Mako templates """ - system_object_types: Dict[str, SystemObjectType] = dict() - values: Dict[str, str] = dict() + system_object_types: Dict[str, SystemObjectType] + values: Dict[str, str] interface_view: InterfaceView deployment_view: DeploymentView diff --git a/tests/test_templateinstantiator.py b/tests/test_templateinstantiator.py index 040a500..e11ca52 100644 --- a/tests/test_templateinstantiator.py +++ b/tests/test_templateinstantiator.py @@ -4,8 +4,7 @@ import pytest import tempfile -from pathlib import Path -from typing import Dict, List +from typing import Dict from templateprocessor.templateinstantiator import TemplateInstantiator from templateprocessor.iv import ( InterfaceView, From 99f23b9aa10601eb4e6e208389646d82056ff82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Thu, 11 Dec 2025 13:04:13 +0100 Subject: [PATCH 27/28] Refactored CLI --- templateprocessor/cli.py | 73 +++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/templateprocessor/cli.py b/templateprocessor/cli.py index edf57a0..feb3c5d 100644 --- a/templateprocessor/cli.py +++ b/templateprocessor/cli.py @@ -13,6 +13,7 @@ from templateprocessor.ivreader import IVReader from templateprocessor.soreader import SOReader from templateprocessor.dvreader import DVReader +from templateprocessor.so import SystemObjectType def parse_arguments() -> argparse.Namespace: @@ -125,6 +126,47 @@ def get_values_dictionary(values: list[str]) -> dict[str, str]: return result +def read_sots(file_names: list[str]) -> dict[str, SystemObjectType]: + sots = {} + so_reader = SOReader() + for sot_file in file_names: + try: + logging.info(f"Reading System Objects from {sot_file}") + name = Path(sot_file).stem + logging.debug(f"-SOT name: {name}") + sos = so_reader.read(sot_file) + sots[name] = sos + except Exception as e: + logging.error(f"Could not read System Objects from {sot_file}") + return sots + + +def instantiate( + instantiator: TemplateInstantiator, + template_file: str, + module_directory: str, + output_directory: str, +): + try: + logging.info(f"Processing template {template_file}") + name = Path(template_file).stem + logging.debug(f"Base name: {name}") + logging.debug(f"Reading template {template_file}") + with open(template_file, "r") as f: + template = f.read() + logging.debug(f"Instantiating template:\n {template}") + instantiated_template = instantiator.instantiate(template, module_directory) + logging.debug(f"Instantiation:\n {instantiated_template}") + output = Path(output_directory) / f"{name}.md" + logging.debug(f"Saving to {output}") + with open(output, "w") as f: + f.write(instantiated_template) + except FileNotFoundError as e: + logging.error(f"File not found: {e.filename}") + except Exception as e: + logging.error(f"Error processing template {template_file}: {e}") + + def main(): """Main entry point for the Template Processor CLI.""" @@ -143,17 +185,13 @@ def main(): logging.info(f"Reading Interface View from {args.iv}") iv = IVReader().read(args.iv) if args.iv else InterfaceView() + logging.info(f"Reading Deployment View from {args.dv}") dv = DVReader().read(args.dv) if args.dv else DeploymentView() - sots = {} - if args.sos: - so_reader = SOReader() - for sot_file in args.sos: - logging.info(f"Reading System Objects from {sot_file}") - name = Path(sot_file).stem - logging.debug(f"-SOT name: {name}") - sos = so_reader.read(sot_file) - sots[name] = sos + + logging.info(f"Reading provided System Objects") + sots = read_sots(args.sos) if args.sos else {} + logging.info(f"Parsing values from {args.value}") values = get_values_dictionary(args.value) @@ -161,22 +199,9 @@ def main(): instantiator = TemplateInstantiator(iv, dv, sots, values) if args.template: + logging.info(f"Instantiating templates") for template_file in args.template: - logging.info(f"Processing template {template_file}") - name = Path(template_file).stem - logging.debug(f"Base name: {name}") - logging.debug(f"Reading template {template_file}") - with open(template_file, "r") as f: - template = f.read() - logging.debug(f"Instantiating template:\n {template}") - instantiated_template = instantiator.instantiate( - template, args.module_directory - ) - logging.debug(f"Instantiation:\n {instantiated_template}") - output = Path(args.output) / f"{name}.md" - logging.debug(f"Saving to {output}") - with open(output, "w") as f: - f.write(instantiated_template) + instantiate(instantiator, template_file, args.module_directory, args.output) return 0 From 6212c6f32489c913ac3d014fbc67f37e5dbd4299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Thu, 11 Dec 2025 15:45:18 +0100 Subject: [PATCH 28/28] Review fixes --- examples/generate_ecss_demo.sh | 8 ++++++++ examples/generate_so_list.sh | 2 +- templateprocessor/cli.py | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh index 84ce76a..ba562eb 100755 --- a/examples/generate_ecss_demo.sh +++ b/examples/generate_ecss_demo.sh @@ -1,18 +1,26 @@ #!/bin/bash mkdir -p output + template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_1_software_static_architecture.pdf output/ecss-e-st-40c_4_1_software_static_architecture.md + template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_2_software_dynamic_architecture.pdf output/ecss-e-st-40c_4_2_software_dynamic_architecture.md + template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_4_4_interfaces_context.pdf output/ecss-e-st-40c_4_4_interfaces_context.md + template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_2_overall_architecture.pdf output/ecss-e-st-40c_5_2_overall_architecture.md + template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_3_software_components_design.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_3_software_components_design.pdf output/ecss-e-st-40c_5_3_software_components_design.md + template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_4_aspects_of_each_component.pdf output/ecss-e-st-40c_5_4_aspects_of_each_component.md + template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_5_5_internal_interface_design.pdf output/ecss-e-st-40c_5_5_internal_interface_design.md + template-processor --verbosity info --value TARGET=ASW --iv demo-project/interfaceview.xml --dv demo-project/deploymentview.dv.xml -o output -t ../data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt pandoc --pdf-engine=pdfroff --output=output/ecss-e-st-40c_6_requirement_traceability.pdf output/ecss-e-st-40c_6_requirement_traceability.md \ No newline at end of file diff --git a/examples/generate_so_list.sh b/examples/generate_so_list.sh index 9fef728..49ad0eb 100755 --- a/examples/generate_so_list.sh +++ b/examples/generate_so_list.sh @@ -1,4 +1,4 @@ #!/bin/bash mkdir -p output -template-processor --verbosity info --sos ../data/events.csv -o output -t so_list.tmplt +template-processor --verbosity info --system-objects ../data/events.csv -o output -t so_list.tmplt pandoc --pdf-engine=pdfroff --output=output/so_list.pdf output/so_list.md \ No newline at end of file diff --git a/templateprocessor/cli.py b/templateprocessor/cli.py index feb3c5d..e8a18ea 100644 --- a/templateprocessor/cli.py +++ b/templateprocessor/cli.py @@ -42,7 +42,7 @@ def parse_arguments() -> argparse.Namespace: parser.add_argument( "-s", - "--sos", + "--system-objects", help="Input System Objects provided as CSV files (each as a separate argument)", metavar="FILE", action="append", @@ -177,7 +177,7 @@ def main(): logging.info("Template Processor") logging.debug(f"Interface View: {args.iv}") logging.debug(f"Deployment View: {args.dv}") - logging.debug(f"System Objects: {args.sos}") + logging.debug(f"System Objects: {args.system_objects}") logging.debug(f"Values: {args.value}") logging.debug(f"Templates: {args.template}") logging.debug(f"Output Directory: {args.output}") @@ -190,7 +190,7 @@ def main(): dv = DVReader().read(args.dv) if args.dv else DeploymentView() logging.info(f"Reading provided System Objects") - sots = read_sots(args.sos) if args.sos else {} + sots = read_sots(args.system_objects) if args.system_objects else {} logging.info(f"Parsing values from {args.value}") values = get_values_dictionary(args.value)