From a6cf1eb77d5586f36ef8ebc2bc46cc0118668eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Tue, 3 Mar 2026 15:37:53 +0100 Subject: [PATCH 1/4] Added IV diagram generation --- ...40c_4_1_software_static_architecture.tmplt | 39 +++++++++++++++++++ templateprocessor/cli.py | 6 ++- templateprocessor/templateinstantiator.py | 8 ++++ 3 files changed, 52 insertions(+), 1 deletion(-) 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 e4a5689..e13a1ec 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,4 +1,36 @@ <% +import os +import subprocess + +## Generate Interface View diagram +_iv_xml = interface_view_path if interface_view_path else None + +iv_diagram_path = None +if _iv_xml: + _iv_image_filename = "iv_logical_architecture.png" + _original_dir = os.getcwd() + _abs_output_dir = os.path.abspath(output_directory) if output_directory else None + try: + _abs_iv_xml = os.path.abspath(_iv_xml) + _result = subprocess.run( + ["spacecreator.AppImage", "--diagramexporter", + "--interfaceview-path", _abs_iv_xml, + "--image-path", _iv_image_filename], + check=False, + capture_output=True + ) + if _result.returncode == 0 and os.path.exists(_iv_image_filename): + if _abs_output_dir and os.path.exists(_abs_output_dir): + import shutil + _dest_path = os.path.join(_abs_output_dir, _iv_image_filename) + shutil.copy2(_iv_image_filename, _dest_path) + iv_diagram_path = _iv_image_filename + else: + _abs_img = os.path.abspath(_iv_image_filename) + iv_diagram_path = os.path.relpath(_abs_img, _original_dir) + except Exception: + pass + ## Data Initialization # Get all functions @@ -56,6 +88,13 @@ for func in deployed_funcs: 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])}. +% if iv_diagram_path: + +The logical architecture is presented in the Figure below: + +![Logical architecture](${iv_diagram_path} "Logical architecture") + +% endif The top-level components are as follows: % for func in interface_view.functions: <% diff --git a/templateprocessor/cli.py b/templateprocessor/cli.py index 908bbdc..8e40f98 100644 --- a/templateprocessor/cli.py +++ b/templateprocessor/cli.py @@ -219,7 +219,11 @@ def main(): values = get_values_dictionary(args.value) logging.info(f"Instantiating the TemplateInstantiator") - instantiator = TemplateInstantiator(iv, dv, sots, values, args.output) + instantiator = TemplateInstantiator( + iv, dv, sots, values, args.output, + interface_view_path=args.iv or "", + deployment_view_path=args.dv or "", + ) logging.info(f"Instantiating the Postprocessor") postprocessor = Postprocessor( diff --git a/templateprocessor/templateinstantiator.py b/templateprocessor/templateinstantiator.py index 3ab6d4d..2e3cb9d 100644 --- a/templateprocessor/templateinstantiator.py +++ b/templateprocessor/templateinstantiator.py @@ -21,6 +21,8 @@ class TemplateInstantiator: interface_view: InterfaceView deployment_view: DeploymentView output_directory: str + interface_view_path: str + deployment_view_path: str def __init__( self, @@ -29,12 +31,16 @@ def __init__( system_object_types: Dict[str, SystemObjectType], values: Dict[str, str], output_directory: str = "", + interface_view_path: str = "", + deployment_view_path: str = "", ): self.system_object_types = system_object_types self.interface_view = interface_view self.deployment_view = deployment_view self.values = values self.output_directory = output_directory + self.interface_view_path = interface_view_path + self.deployment_view_path = deployment_view_path def instantiate(self, template: str, context_directory: str) -> str: mako_template = Template(text=template, module_directory=context_directory) @@ -45,6 +51,8 @@ def instantiate(self, template: str, context_directory: str) -> str: "deployment_view": self.deployment_view, "values": self.values, "output_directory": self.output_directory, + "interface_view_path": self.interface_view_path, + "deployment_view_path": self.deployment_view_path, } instantiated_text = str(mako_template.render(**context)) From b4499cb6c78ad426ead5c9ee48d4e650cf32bb49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Tue, 3 Mar 2026 15:41:28 +0100 Subject: [PATCH 2/4] make format --- templateprocessor/cli.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/templateprocessor/cli.py b/templateprocessor/cli.py index 8e40f98..9dc8ce3 100644 --- a/templateprocessor/cli.py +++ b/templateprocessor/cli.py @@ -220,7 +220,11 @@ def main(): logging.info(f"Instantiating the TemplateInstantiator") instantiator = TemplateInstantiator( - iv, dv, sots, values, args.output, + iv, + dv, + sots, + values, + args.output, interface_view_path=args.iv or "", deployment_view_path=args.dv or "", ) From df439dc63e9bd47f8b7bb2334893bc9e826ff22a Mon Sep 17 00:00:00 2001 From: Lurkerpas Date: Tue, 3 Mar 2026 15:52:43 +0100 Subject: [PATCH 3/4] Update data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../ecss-e-st-40c_4_1_software_static_architecture.tmplt | 4 ++-- 1 file changed, 2 insertions(+), 2 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 e13a1ec..2843db5 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 @@ -22,8 +22,8 @@ if _iv_xml: if _result.returncode == 0 and os.path.exists(_iv_image_filename): if _abs_output_dir and os.path.exists(_abs_output_dir): import shutil - _dest_path = os.path.join(_abs_output_dir, _iv_image_filename) - shutil.copy2(_iv_image_filename, _dest_path) + shutil.copy2(_iv_image_filename, os.path.join(_abs_output_dir, _iv_image_filename)) + os.remove(_iv_image_filename) iv_diagram_path = _iv_image_filename else: _abs_img = os.path.abspath(_iv_image_filename) From 1f2ab4bde04ccca499e95e1802c90669be514685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kurowski?= Date: Tue, 3 Mar 2026 16:02:09 +0100 Subject: [PATCH 4/4] Added unit tests --- tests/test_templateinstantiator.py | 74 ++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/test_templateinstantiator.py b/tests/test_templateinstantiator.py index e11ca52..67de2d3 100644 --- a/tests/test_templateinstantiator.py +++ b/tests/test_templateinstantiator.py @@ -554,3 +554,77 @@ def test_instantiate_template_with_values(self): assert "Version: 2.1.0" in result assert "Author: Test Author" in result assert "Description: Test project description" in result + + def test_interface_view_path_stored(self): + """interface_view_path passed to constructor is stored on the instantiator.""" + 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, + {}, + interface_view_path="/path/to/interfaceview.xml", + ) + + assert instantiator.interface_view_path == "/path/to/interfaceview.xml" + + def test_deployment_view_path_stored(self): + """deployment_view_path passed to constructor is stored on the instantiator.""" + 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, + {}, + deployment_view_path="/path/to/deploymentview.dv.xml", + ) + + assert instantiator.deployment_view_path == "/path/to/deploymentview.dv.xml" + + def test_interface_view_path_available_in_template(self): + """interface_view_path is accessible as a variable inside templates.""" + 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, + {}, + interface_view_path="/some/project/interfaceview.xml", + ) + + template = "IV path: ${interface_view_path}" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert result == "IV path: /some/project/interfaceview.xml" + + def test_deployment_view_path_available_in_template(self): + """deployment_view_path is accessible as a variable inside templates.""" + 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, + {}, + deployment_view_path="/some/project/deploymentview.dv.xml", + ) + + template = "DV path: ${deployment_view_path}" + + with tempfile.TemporaryDirectory() as tmpdir: + result = instantiator.instantiate(template, tmpdir) + + assert result == "DV path: /some/project/deploymentview.dv.xml"