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]
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/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..a8274c2
--- /dev/null
+++ b/data/ecss-template/ecss-e-st-40c_4_1_software_static_architecture.tmplt
@@ -0,0 +1,82 @@
+<%
+## 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_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..adbfb7b
--- /dev/null
+++ b/data/ecss-template/ecss-e-st-40c_4_2_software_dynamic_architecture.tmplt
@@ -0,0 +1,74 @@
+<%
+## 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/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..bfc8968
--- /dev/null
+++ b/data/ecss-template/ecss-e-st-40c_4_4_interfaces_context.tmplt
@@ -0,0 +1,165 @@
+<%
+## 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)
+
+# 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:
+ 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
+ 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):
+ external_connections.append(connection)
+
+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_2_overall_architecture.tmplt b/data/ecss-template/ecss-e-st-40c_5_2_overall_architecture.tmplt
new file mode 100644
index 0000000..7ad8a05
--- /dev/null
+++ 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_components_design.tmplt b/data/ecss-template/ecss-e-st-40c_5_3_software_components_design.tmplt
new file mode 100644
index 0000000..467b979
--- /dev/null
+++ b/data/ecss-template/ecss-e-st-40c_5_3_software_components_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/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..d878524
--- /dev/null
+++ b/data/ecss-template/ecss-e-st-40c_5_4_aspects_of_each_component.tmplt
@@ -0,0 +1,84 @@
+<%
+## 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 chapters summarize aspects of each component.
+
+% for func in deployed_funcs:
+
+${"#"} ${func.name}
+
+Component Identifier: ${func.name}
+
+Type: TASTE ${func.language.value} Function
+
+Purpose: ${func.comment}
+
+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/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..dd2ecac
--- /dev/null
+++ b/data/ecss-template/ecss-e-st-40c_5_5_internal_interface_design.tmplt
@@ -0,0 +1,166 @@
+<%
+## 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)
+
+
+
+# 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/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..816aee3
--- /dev/null
+++ b/data/ecss-template/ecss-e-st-40c_6_requirement_traceability.tmplt
@@ -0,0 +1,63 @@
+<%
+## 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 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 requirements
+req_map = {}
+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
+
+| 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 deployed_funcs:
+| ${func.name} | ${",".join(func.requirement_ids)} |
+% endfor
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/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..32ec9c8
--- /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..4e7d949
--- /dev/null
+++ b/examples/demo-project/interfaceview.xml
@@ -0,0 +1,574 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
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/examples/generate_ecss_demo.sh b/examples/generate_ecss_demo.sh
new file mode 100755
index 0000000..ba562eb
--- /dev/null
+++ b/examples/generate_ecss_demo.sh
@@ -0,0 +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
new file mode 100755
index 0000000..49ad0eb
--- /dev/null
+++ b/examples/generate_so_list.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+mkdir -p output
+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/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..d51881a
--- /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/examples/so_list.tmplt b/examples/so_list.tmplt
new file mode 100644
index 0000000..e80082c
--- /dev/null
+++ b/examples/so_list.tmplt
@@ -0,0 +1,10 @@
+### 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..e8a18ea 100644
--- a/templateprocessor/cli.py
+++ b/templateprocessor/cli.py
@@ -2,13 +2,21 @@
Command Line Interface for Template Processor
"""
+import logging
import argparse
+from pathlib import Path
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
+from templateprocessor.so import SystemObjectType
-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,
@@ -20,23 +28,180 @@ def main():
parser.add_argument(
"-i",
- "--input",
- help="Input data file (e.g., TASTE Interface View data)",
+ "--iv",
+ help="Input Interface View",
+ metavar="FILE",
+ )
+
+ parser.add_argument(
+ "-d",
+ "--dv",
+ help="Input Deployment View",
+ metavar="FILE",
+ )
+
+ parser.add_argument(
+ "-s",
+ "--system-objects",
+ help="Input System Objects provided as CSV files (each as a separate argument)",
+ metavar="FILE",
+ 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",
+ help="Template file to process (each as a separate argument)",
metavar="FILE",
+ action="append",
+ )
+
+ parser.add_argument(
+ "-m",
+ "--module-directory",
+ help="Module directory for Mako templating engine",
+ metavar="FILE",
+ )
+
+ 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 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 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 ValueError(f"Name in [{pair}] is empty")
+ # value can be empty
+ result[name] = value
+ 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."""
+
+ 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"Deployment View: {args.dv}")
+ 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}")
+ 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()
+
+ logging.info(f"Reading Deployment View from {args.dv}")
+ dv = DVReader().read(args.dv) if args.dv else DeploymentView()
+
+ logging.info(f"Reading provided System Objects")
+ 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)
+
+ logging.info(f"Instantiating the TemplateInstantiator")
+ instantiator = TemplateInstantiator(iv, dv, sots, values)
- print("Template Processor - Not yet implemented")
- print(f"Version: {__version__}")
+ if args.template:
+ logging.info(f"Instantiating templates")
+ for template_file in args.template:
+ instantiate(instantiator, template_file, args.module_directory, args.output)
return 0
diff --git a/templateprocessor/iv.py b/templateprocessor/iv.py
index 734e9a1..0bffeb5 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
@@ -76,6 +78,7 @@ class FunctionInterface:
id: str
name: str
+ comment: str
kind: InterfaceKind
enable_multicast: bool = True
layer: str = "default"
@@ -92,6 +95,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
@@ -122,6 +126,7 @@ class Function:
id: str
name: str
+ comment: str
is_type: bool
language: Optional[Language] = None
default_implementation: str = "default"
@@ -138,6 +143,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
@@ -200,10 +206,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)
diff --git a/templateprocessor/ivreader.py b/templateprocessor/ivreader.py
index 493cda7..293abed 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
@@ -134,6 +135,9 @@ def _parse_function(self, elem: ET.Element) -> Function:
if elem.get("type_language")
else None
),
+ requirement_ids=[
+ rid for rid in elem.get("requirement_ids", "").split(",") if rid
+ ],
)
# Parse properties
@@ -175,6 +179,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"),
@@ -192,6 +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=[
+ rid for rid in elem.get("requirement_ids", "").split(",") if rid
+ ],
)
# Parse input parameters
diff --git a/templateprocessor/templateinstantiator.py b/templateprocessor/templateinstantiator.py
new file mode 100644
index 0000000..25f0c62
--- /dev/null
+++ b/templateprocessor/templateinstantiator.py
@@ -0,0 +1,47 @@
+"""
+Template Instantiator.
+
+This module is responsible for instantiating Mako templates using the provided data.
+"""
+
+from templateprocessor.iv import InterfaceView
+from templateprocessor.dv import DeploymentView
+from templateprocessor.so import SystemObjectType
+from typing import Dict
+from mako.template import Template
+
+
+class TemplateInstantiator:
+ """
+ Instantiator of Mako templates
+ """
+
+ system_object_types: Dict[str, SystemObjectType]
+ values: Dict[str, str]
+ interface_view: InterfaceView
+ deployment_view: DeploymentView
+
+ def __init__(
+ self,
+ 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)
+
+ context = {
+ "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))
+ return instantiated_text
diff --git a/tests/Makefile b/tests/Makefile
index bd4d2ba..6eff3a1 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -4,7 +4,8 @@ PYTHON ?= python3
TESTS = \
test_ivreader.py \
test_dvreader.py \
- test_soreader.py
+ test_soreader.py \
+ test_templateinstantiator.py
.PHONY: \
check
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
diff --git a/tests/test_templateinstantiator.py b/tests/test_templateinstantiator.py
new file mode 100644
index 0000000..e11ca52
--- /dev/null
+++ b/tests/test_templateinstantiator.py
@@ -0,0 +1,556 @@
+"""
+Tests for TemplateInstantiator class
+"""
+
+import pytest
+import tempfile
+from typing import Dict
+from templateprocessor.templateinstantiator import TemplateInstantiator
+from templateprocessor.iv import (
+ InterfaceView,
+ Function,
+ Language,
+ ProvidedInterface,
+ RequiredInterface,
+ InterfaceKind,
+ InputParameter,
+ OutputParameter,
+ Encoding,
+)
+from templateprocessor.so import SystemObjectType, SystemObject
+from templateprocessor.dv import (
+ DeploymentView,
+ Node,
+ Partition,
+ DeploymentFunction,
+ Device,
+ Connection,
+ Message,
+)
+
+
+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",
+ 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 = [
+ InputParameter(name="input1", type="MyInt", encoding=Encoding.NATIVE)
+ ]
+ func.provided_interfaces = [pi]
+
+ # Add a required interface
+ ri = RequiredInterface(
+ id="ri_1",
+ name="test_ri",
+ comment="No Comment",
+ 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() -> Dict[str, 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]
+
+ sots = dict()
+ sots["events"] = events
+ sots["params"] = params
+
+ 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, dv, 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()
+ dv = self.create_sample_deployment_view()
+ so_types = self.create_sample_system_object_types()
+ instantiator = TemplateInstantiator(iv, dv, 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()
+ dv = self.create_sample_deployment_view()
+ so_types = self.create_sample_system_object_types()
+ instantiator = TemplateInstantiator(iv, dv, 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()
+ dv = self.create_sample_deployment_view()
+ so_types = self.create_sample_system_object_types()
+ instantiator = TemplateInstantiator(iv, dv, 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()
+ dv = self.create_sample_deployment_view()
+ so_types = self.create_sample_system_object_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():
+ [${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 "[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):
+ """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, dv, so_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']}
+% endif
+% endfor
+% endfor"""
+
+ with tempfile.TemporaryDirectory() as tmpdir:
+ result = instantiator.instantiate(template, tmpdir)
+ 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="")
+ dv = self.create_sample_deployment_view()
+
+ so_types = {}
+
+ instantiator = TemplateInstantiator(iv, dv, 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()
+ dv = self.create_sample_deployment_view()
+ so_types = self.create_sample_system_object_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)
+total_instances = sum(len(so.instances) for so in system_object_types.values())
+%>
+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
+
+ 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
+
+ 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