Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat derived interfaces #94

Merged
merged 8 commits into from
May 15, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: Add non-existent ComponentPorts
  • Loading branch information
ewuerger committed May 7, 2024
commit 1733ff1274596d3d30fcf206d904a9f52f037f96
25 changes: 15 additions & 10 deletions capellambse_context_diagrams/collectors/default.py
Original file line number Diff line number Diff line change
@@ -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
@@ -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]
@@ -310,24 +314,25 @@ def _derive_from_functions(

# TODO: Even out derived interfaces on each side
ewuerger marked this conversation as resolved.
Show resolved Hide resolved

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}"
ewuerger marked this conversation as resolved.
Show resolved Hide resolved
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],
}
@@ -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
@@ -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
@@ -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:
21 changes: 8 additions & 13 deletions capellambse_context_diagrams/serializers.py
Original file line number Diff line number Diff line change
@@ -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__)

@@ -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
@@ -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
]

@@ -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 = [
@@ -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)

5 changes: 2 additions & 3 deletions tests/test_context_diagrams.py
Original file line number Diff line number Diff line change
@@ -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
Loading