Skip to content

Commit

Permalink
feat: Feat-hide-functions (#113)
Browse files Browse the repository at this point in the history
Add interface section and document `hide_functions`.
  • Loading branch information
ewuerger authored Jun 27, 2024
1 parent de72095 commit d019538
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 57 deletions.
31 changes: 19 additions & 12 deletions capellambse_context_diagrams/collectors/exchanges.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,18 @@ def collect_context(
components.append(c)
incomings |= incs
outgoings |= outs
return (
{
"element": comp,
"functions": functions,
"components": components,
},
incomings,
outgoings,
)

start = {
"element": comp,
"functions": functions,
"components": components,
}
if self.diagram.hide_functions:
start["functions"] = []
incomings = {}
outgoings = {}

return start, incomings, outgoings

def make_ports_and_update_children_size(
self,
Expand Down Expand Up @@ -155,7 +158,8 @@ def make_ports_and_update_children_size(
child.height = height
stack_height += makers.NEIGHBOR_VMARGIN + height

data.height = stack_height
if stack_height > 0:
data.height = stack_height

@abc.abstractmethod
def collect(self) -> None:
Expand Down Expand Up @@ -222,6 +226,9 @@ def get_capella_order(
def make_boxes(cntxt: dict[str, t.Any]) -> _elkjs.ELKInputChild | None:
comp = cntxt["element"]
functions = cntxt["functions"]
if self.diagram.hide_functions:
functions = []

components = cntxt["components"]
if comp.uuid not in made_children:
children = [
Expand Down Expand Up @@ -278,8 +285,8 @@ def make_boxes(cntxt: dict[str, t.Any]) -> _elkjs.ELKInputChild | None:
if right_child := make_boxes(right_context):
self.data.children.append(right_child)
self.right = right_child
except AttributeError:
pass
except AttributeError as error:
logger.exception("Interface collection failed: \n%r", str(error))

def add_interface(self) -> None:
ex_data = generic.ExchangeData(
Expand Down
9 changes: 9 additions & 0 deletions capellambse_context_diagrams/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,16 +380,25 @@ def __init__(
class_: str,
obj: common.GenericElement,
include_interface: bool = False,
hide_functions: bool = False,
**kw,
) -> None:
self.include_interface = include_interface
self.hide_functions = hide_functions
super().__init__(class_, obj, **kw, display_symbols_as_boxes=True)

@property
def name(self) -> str: # type: ignore
return f"Interface Context of {self.target.name}"

def _create_diagram(self, params: dict[str, t.Any]) -> cdiagram.Diagram:
for param_name in ("include_interface", "hide_functions"):
if override := params.pop(param_name, False):
setattr(self, param_name, override)

if self.hide_functions:
self.include_interface = True

params["elkdata"] = exchanges.get_elkdata_for_exchanges(
self, exchanges.InterfaceContextCollector, params
)
Expand Down
11 changes: 11 additions & 0 deletions docs/gen_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ def generate_derived_image() -> None:
print(diag.render("svg", **params), file=fd)


def generate_interface_with_hide_functions_image():
uuid = interface_context_diagram_uuids["Interface"][0]
diag: context.ContextDiagram = model.by_uuid(uuid).context_diagram
params = {"hide_functions": True}
with mkdocs_gen_files.open(
f"{str(dest / diag.name)}-hide-functions.svg", "w"
) as fd:
print(diag.render("svg", **params), file=fd)


generate_index_images()
generate_hierarchy_image()
generate_no_symbol_images()
Expand All @@ -202,3 +212,4 @@ def generate_derived_image() -> None:
generate_realization_view_images()
generate_data_flow_image()
generate_derived_image()
generate_interface_with_hide_functions_image()
49 changes: 8 additions & 41 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,52 +213,19 @@ Hierarchy is identified and supported:
<figcaption>Context diagram of Hierarchy LogicalComponenet with type [LAB]</figcaption>
</figure>

### Interfaces (aka ComponentExchanges)

The data is collected by [get_elkdata_for_exchanges][capellambse_context_diagrams.collectors.exchanges.get_elkdata_for_exchanges] which is using the [`InterfaceContextCollector`][capellambse_context_diagrams.collectors.exchanges.InterfaceContextCollector] underneath.

??? example "[`fa.ComponentExchange`][capellambse.model.crosslayer.fa.ComponentExchange]"

``` py
import capellambse

model = capellambse.MelodyModel("tests/data/ContextDiagram.aird")
diag = model.by_uuid("3ef23099-ce9a-4f7d-812f-935f47e7938d").context_diagram
diag.render("svgdiagram").save(pretty=True)
```
<figure markdown>
<img src="assets/images/Interface Context of Left to right.svg" width="1000000">
<figcaption>Interface context diagram of Left to right LogicalComponentExchange with type [LAB]</figcaption>
</figure>

??? example "Include the interface the ([`fa.ComponentExchange`][capellambse.model.crosslayer.fa.ComponentExchange])"

``` py
import capellambse

model = capellambse.MelodyModel("tests/data/ContextDiagram.aird")
diag = model.by_uuid("fbb7f735-3c1f-48de-9791-179d35ca7b98").context_diagram
diag.render("svgdiagram", include_interface=True).save(pretty=True)
```
<figure markdown>
<img src="assets/images/Interface Context of Interface.svg" width="1000000">
<figcaption>Interface context diagram of Interface LogicalComponentExchange with type [LAB]</figcaption>
</figure>

!!! warning "Interface context only supported for the LogicalComponentExchanges"

### Customized edge routing

!!! note "Custom routing"
The routing differs from [ELK's Layered Algorithm](https://www.eclipse.org/elk/reference/algorithms/org-eclipse-elk-layered.html): The flow display is disrupted!
We configure exchanges such that they appear in between the context
participants. This decision breaks the display of data flow which is one
of the main aims of ELK's Layered algorithm. However this lets counter
flow exchanges routes lengths and bendpoints increase.

The routing differs from [ELK's Layered Algorithm](https://www.eclipse.org/elk/reference/algorithms/org-eclipse-elk-layered.html): The flow display is disrupted!
We configure exchanges such that they appear in between the context
participants. This decision breaks the display of data flow which is one
of the main aims of ELK's Layered algorithm. However this lets counter
flow exchanges routes lengths and bendpoints increase.

<figure markdown>
<img src="assets/images/Context of Weird guy.svg" width="1000000">
<figcaption>Context diagram of Weird guy SystemFunction</figcaption>
<img src="assets/images/Context of Weird guy.svg" width="1000000">
<figcaption>Context diagram of Weird guy SystemFunction</figcaption>
</figure>

---
Expand Down
56 changes: 56 additions & 0 deletions docs/interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!--
~ SPDX-FileCopyrightText: 2022 Copyright DB InfraGO AG and the capellambse-context-diagrams contributors
~ SPDX-License-Identifier: Apache-2.0
-->

# Interfaces (aka ComponentExchanges)

The data is collected by [get_elkdata_for_exchanges][capellambse_context_diagrams.collectors.exchanges.get_elkdata_for_exchanges] which is using the [`InterfaceContextCollector`][capellambse_context_diagrams.collectors.exchanges.InterfaceContextCollector] underneath.

You can render an interface context view just with `context_diagram` on any
[`fa.ComponentExchange`][capellambse.model.crosslayer.fa.ComponentExchange]:

``` py
import capellambse

model = capellambse.MelodyModel("tests/data/ContextDiagram.aird")
diag = model.by_uuid("3ef23099-ce9a-4f7d-812f-935f47e7938d").context_diagram
diag.render("svgdiagram").save(pretty=True)
```

<figure markdown>
<img src="../assets/images/Interface Context of Left to right.svg" width="1000000">
<figcaption>Interface context diagram of Left to right LogicalComponentExchange with type [LAB]</figcaption>
</figure>

## Include the interface itself in the context
??? example "Include the interface in the Interface Context"

``` py
import capellambse

model = capellambse.MelodyModel("tests/data/ContextDiagram.aird")
diag = model.by_uuid("fbb7f735-3c1f-48de-9791-179d35ca7b98").context_diagram
diag.render("svgdiagram", include_interface=True).save(pretty=True)
```
<figure markdown>
<img src="../assets/images/Interface Context of Interface.svg" width="1000000">
<figcaption>Interface context diagram of Interface LogicalComponentExchange with type [LAB]</figcaption>
</figure>

## Hide functional model elements from the context
??? example "Hide functions and functional exchanges in the Interface Context"

``` py
import capellambse

model = capellambse.MelodyModel("tests/data/ContextDiagram.aird")
diag = model.by_uuid("fbb7f735-3c1f-48de-9791-179d35ca7b98").context_diagram
diag.render("svgdiagram", hide_functions=True).save(pretty=True)
```
<figure markdown>
<img src="../assets/images/Interface Context of Interface-hide-functions.svg" width="1000000">
<figcaption>Interface context diagram of Interface LogicalComponentExchange with type [LAB]</figcaption>
</figure>

!!! warning "Interface context only supported for the LogicalComponentExchanges"
10 changes: 6 additions & 4 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,18 @@ nav:
- Quickstart: quickstart.md
- Credits: credits.md
- License: license.md
- Extras:
- Filters: extras/filters.md
- Styling: extras/styling.md
- 🔥 Derived 🔥: extras/derived.md
- Interface Context View:
- Overview: interface.md
- Tree View:
- Overview: tree_view.md
- Realization View:
- Overview: realization_view.md
- DataFlow View:
- Overview: data_flow_view.md
- Extras:
- Filters: extras/filters.md
- Styling: extras/styling.md
- Derived: extras/derived.md
- Code Reference: reference/

extra_css:
Expand Down
18 changes: 18 additions & 0 deletions tests/test_interface_diagrams.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,21 @@ def test_interface_diagram_with_included_interface(
diag = obj.context_diagram.render(None, include_interface=True)

assert diag[TEST_INTERFACE_UUID]


def test_interface_diagram_with_hide_functions(
model: capellambse.MelodyModel,
) -> None:
obj = model.by_uuid(TEST_INTERFACE_UUID)

diag = obj.context_diagram.render(None, hide_functions=True)
obj.context_diagram.render("svgdiagram", hide_functions=True).save(
pretty=True
)

for uuid in (
"fbfb2b20-b711-4211-9b75-25e38390cdbc", # LogicalFunction
"2b30434f-a087-40f1-917b-c9d0af15be23", # FunctionalExchange
):
with pytest.raises(KeyError):
diag[uuid] # pylint: disable=pointless-statement

0 comments on commit d019538

Please sign in to comment.