Skip to content

Commit

Permalink
feat: Add non-existent ComponentPorts
Browse files Browse the repository at this point in the history
  • Loading branch information
ewuerger committed May 7, 2024
1 parent b5c1dd9 commit 1733ff1
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 32 deletions.
25 changes: 15 additions & 10 deletions capellambse_context_diagrams/collectors/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
from capellambse import helpers
from capellambse.model import common
from capellambse.model.crosslayer import cs, fa
from capellambse.model.layers import ctx, la
from capellambse.model.layers import ctx as sa
from capellambse.model.layers import la
from capellambse.model.modeltypes import DiagramType as DT

from .. import _elkjs, context
Expand All @@ -23,6 +24,9 @@ def collector(
diagram: context.ContextDiagram, params: dict[str, t.Any] | None = None
) -> _elkjs.ELKInputData:
"""Collect context data from ports of centric box."""
diagram.display_derived_interfaces = (params or {}).pop(
"display_derived_interfaces", diagram.display_derived_interfaces
)
data = generic.collector(diagram, no_symbol=True)
ports = port_collector(diagram.target, diagram.type)
centerbox = data["children"][0]
Expand Down Expand Up @@ -310,24 +314,25 @@ def _derive_from_functions(

# TODO: Even out derived interfaces on each side

for i, (uuid, derived_component) in enumerate(components.items()):
centerbox = data["children"][0]
for i, (uuid, derived_component) in enumerate(components.items(), 1):
box = makers.make_box(
derived_component,
no_symbol=diagram.display_symbols_as_boxes,
)
class_ = type(derived_comp).__name__
box["id"] = comp_uuid = f"__Derived-{class_}_{uuid}"
box["id"] = f"__Derived-{class_}:{uuid}"
data["children"].append(box)
source_id = f"__Derived-CP_INOUT:{i}"
target_id = f"__Derived-CP_INOUT:{-i}"
box.setdefault("ports", []).append(makers.make_port(source_id))
centerbox.setdefault("ports", []).append(makers.make_port(target_id))
if i % 2 == 0:
source_id = comp_uuid
target_id = diagram.target.uuid
else:
source_id = diagram.target.uuid
target_id = comp_uuid
source_id, target_id = target_id, source_id

data["edges"].append(
{
"id": f"__Derived-ComponentExchange_{i}",
"id": f"__Derived-ComponentExchange:{i}",
"sources": [source_id],
"targets": [target_id],
}
Expand All @@ -341,5 +346,5 @@ def _derive_from_functions(

DERIVATORS = {
la.LogicalComponent: _derive_from_functions,
ctx.SystemComponent: _derive_from_functions,
sa.SystemComponent: _derive_from_functions,
}
18 changes: 12 additions & 6 deletions capellambse_context_diagrams/collectors/makers.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@
FAULT_PAD = 10
"""Height adjustment for labels."""
BOX_TO_SYMBOL = (
layers.ctx.Capability,
layers.oa.OperationalCapability,
layers.ctx.Mission,
layers.ctx.SystemComponent,
layers.ctx.Capability.__name__,
layers.oa.OperationalCapability.__name__,
layers.ctx.Mission.__name__,
layers.ctx.SystemComponent.__name__,
"SystemHumanActor",
"SystemActor",
)
"""
Types that need to be converted to symbols during serialization if
Expand Down Expand Up @@ -189,9 +191,13 @@ def calculate_height_and_width(
return width, max(height, _height)


def is_symbol(obj: common.GenericElement) -> bool:
def is_symbol(obj: str | common.GenericElement | None) -> bool:
"""Check if given `obj` is rendered as a Symbol instead of a Box."""
return isinstance(obj, BOX_TO_SYMBOL)
if obj is None:
return False
elif isinstance(obj, str):
return obj in BOX_TO_SYMBOL
return type(obj).__name__ in BOX_TO_SYMBOL


def make_port(uuid: str) -> _elkjs.ELKInputPort:
Expand Down
21 changes: 8 additions & 13 deletions capellambse_context_diagrams/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
from capellambse import diagram
from capellambse.svg import decorations

from . import _elkjs, collectors, context
from . import _elkjs, context
from .collectors import makers

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -116,7 +117,7 @@ class type that stores all previously named classes.
styleclass: str | None
derived = False
if child["id"].startswith("__"):
styleclass, uuid = child["id"][2:].split("_", 1)
styleclass, uuid = child["id"][2:].split(":", 1)
if styleclass.startswith("Derived-"):
styleclass = styleclass.removeprefix("Derived-")
derived = True
Expand All @@ -128,18 +129,12 @@ class type that stores all previously named classes.
element: diagram.Box | diagram.Edge | diagram.Circle
if child["type"] in {"node", "port"}:
assert parent is None or isinstance(parent, diagram.Box)
has_symbol_cls = False
try:
obj = self.model.by_uuid(uuid)
has_symbol_cls = collectors.makers.is_symbol(obj)
except KeyError:
logger.error("ModelObject could not be found: '%s'", uuid)

has_symbol_cls = makers.is_symbol(styleclass)
is_port = child["type"] == "port"
box_type = ("box", "symbol")[
is_port
or has_symbol_cls
and not self._diagram.target == obj
and not self._diagram.target.uuid == uuid
and not self._diagram.display_symbols_as_boxes
]

Expand Down Expand Up @@ -170,11 +165,11 @@ class type that stores all previously named classes.

source_id = child["sourceId"]
if source_id.startswith("__"):
source_id = source_id[2:].split("_", 1)[-1]
source_id = source_id[2:].split(":", 1)[-1]

target_id = child["targetId"]
if target_id.startswith("__"):
target_id = target_id[2:].split("_", 1)[-1]
target_id = target_id[2:].split(":", 1)[-1]

if child["routingPoints"]:
refpoints = [
Expand Down Expand Up @@ -275,7 +270,7 @@ def get_styleclass(self, uuid: str) -> str | None:
except KeyError:
if not uuid.startswith("__"):
return None
return uuid[2:].split("_", 1)[0]
return uuid[2:].split(":", 1)[0]
else:
return diagram.get_styleclass(melodyobj)

Expand Down
5 changes: 2 additions & 3 deletions tests/test_context_diagrams.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ def test_context_diagram_with_derived_interfaces(

diag = obj.context_diagram
diag.display_derived_interfaces = True
diagram = diag.render(None)

diag.render("svgdiagram").save(pretty=True)

assert len(diag.nodes) > 5
assert len(diagram) > 5

0 comments on commit 1733ff1

Please sign in to comment.