Skip to content

derived

DERIVATORS module-attribute 🔗

DERIVATORS: dict[type[ModelElement], DerivatorFunction] = {
    LogicalComponent: derive_from_functions,
    SystemComponent: derive_from_functions,
}

Supported objects to build derived contexts for.

derive_from_functions 🔗

derive_from_functions(
    diagram: ContextDiagram,
    boxes: dict[str, ELKInputChild],
    edges: dict[str, ELKInputEdge],
) -> None

Derive Components from allocated functions of the context target.

A Component, a ComponentExchange and two ComponentPorts are added to data. These elements are prefixed with Derived- to receive special styling in the serialization step.

Source code in src/capellambse_context_diagrams/builders/derived.py
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def derive_from_functions(
    diagram: context.ContextDiagram,
    boxes: dict[str, _elkjs.ELKInputChild],
    edges: dict[str, _elkjs.ELKInputEdge],
) -> None:
    """Derive Components from allocated functions of the context target.

    A Component, a ComponentExchange and two ComponentPorts are added
    to ``data``. These elements are prefixed with ``Derived-`` to
    receive special styling in the serialization step.
    """
    assert isinstance(diagram.target, cs.Component)
    ports: list[m.ModelElement] = []
    for fnc in diagram.target.allocated_functions:
        inc, out = _generic.port_collector(fnc, diagram.type)
        ports.extend((inc | out).values())

    derived_components: dict[str, cs.Component] = {}
    for port in ports:
        for fex in port.exchanges:
            if isinstance(port, fa.FunctionOutputPort):
                attr = "target"
            else:
                attr = "source"

            try:
                derived_comp = getattr(fex, attr).owner.owner
                if (
                    derived_comp == diagram.target
                    or derived_comp.uuid in boxes
                ):
                    continue

                if derived_comp.uuid not in derived_components:
                    derived_components[derived_comp.uuid] = derived_comp
            except AttributeError:  # No owner of owner.
                pass

    # Idea: Include flow direction of derived interfaces from all functional
    # exchanges. Mixed means bidirectional. Just even out bidirectional
    # interfaces and keep flow direction of others.

    centerbox = boxes[diagram.target.uuid]
    i = 0
    for i, (uuid, derived_component) in enumerate(
        derived_components.items(), 1
    ):
        box = _makers.make_box(
            derived_component,
            no_symbol=diagram._display_symbols_as_boxes,
        )
        class_ = diagram.serializer.get_styleclass(derived_component.uuid)
        box.id = f"{_makers.STYLECLASS_PREFIX}-{class_}:{uuid}"
        boxes[uuid] = box
        source_id = f"{_makers.STYLECLASS_PREFIX}-CP_INOUT:{i}"
        target_id = f"{_makers.STYLECLASS_PREFIX}-CP_INOUT:{-i}"
        box.ports.append(_makers.make_port(source_id))
        centerbox.ports.append(_makers.make_port(target_id))
        if i % 2 == 0:
            source_id, target_id = target_id, source_id

        uid = f"{_makers.STYLECLASS_PREFIX}-ComponentExchange:{i}"
        edges[uid] = _elkjs.ELKInputEdge(
            id=uid,
            sources=[source_id],
            targets=[target_id],
        )

    centerbox.height += (
        _makers.PORT_PADDING
        + (_makers.PORT_SIZE + _makers.PORT_PADDING) * i // 2
    )