Extracting Connections with Automator

Hello, I’m trying to extract connections from the model and I’m wondering if there is a convenient method to do?

Here’s an example model in its textual and graphical form (I hope it’s modeled correctly). What I would like to extract from the model is the following information:

  • C connected to A
  • C connected to B
  • C connected to D
  • C connected to E

What’s the best approach to do that?

Thanks!

Tom

 package InterfaceExample {

    port def MyPort;
    part def A { port p:MyPort; }
    part def B { port p:MyPort; }
    part def C { port p1:MyPort; port p2:MyPort;}
    part def D { port p:MyPort; }
    part def E { port p:MyPort; }

    part def SubsystemA {
        part a : A;
        part b : B;
        port p : MyPort;

        binding bind p = a.p;
        binding bind p = b.p;
    }

    part def SubsystemB {
        part c : C;
        part d : D;
        part e : E;

        port p : MyPort;

        binding bind c.p1 = p;
        interface connect c.p2 to d.p;
        interface connect c.p2 to e.p;
    }

    part def System {
        part sa : SubsystemA;
        part sb : SubsystemB;

        interface connect sb.p to sa.p;
    }
}

Hi,

The easiest, and likely most performant, approach to find elements of specific type is to use Model.nodes (or Document.nodes/Document.all_nodes):

import syside

SRC = """\
package InterfaceExample {

    port def MyPort;
    part def A { port p:MyPort; }
    part def B { port p:MyPort; }
    part def C { port p1:MyPort; port p2:MyPort;}
    part def D { port p:MyPort; }
    part def E { port p:MyPort; }

    part def SubsystemA {
        part a : A;
        part b : B;
        port p : MyPort;

        binding bind p = a.p;
        binding bind p = b.p;
    }

    part def SubsystemB {
        part c : C;
        part d : D;
        part e : E;

        port p : MyPort;

        binding bind c.p1 = p;
        interface connect c.p2 to d.p;
        interface connect c.p2 to e.p;
    }

    part def System {
        part sa : SubsystemA;
        part sb : SubsystemB;

        interface connect sb.p to sa.p;
    }
}
"""

model, _ = syside.load_model(sysml_source=SRC)

for connector in model.elements(syside.ConnectorAsUsage, include_subtypes=True):
    ends = connector.related_elements.collect()

    # unwrap feature chainings with `feature_target`
    print(f"{ends[0].feature_target} -> {ends[1].feature_target}")

Thanks! That’s really easy this way.

If I change the last line of your script to:

print(f"{ends[0].feature_target.owner.name} --{connector.cst_node.type}--> {ends[1].feature_target.owner.name}")

I get the following output (which is nearly what I’m looking for):

C --InterfaceUsage–> D
C --InterfaceUsage–> E
SubsystemB --InterfaceUsage–> SubsystemA
SubsystemA --BindingConnectorAsUsage–> A
SubsystemA --BindingConnectorAsUsage–> B
C --BindingConnectorAsUsage–> SubsystemB

Is there an easy way to “remove” the binding connector usages and the “inbetween” interfaces? The logic I’m thinking of is:

C --BindingConnectorAsUsage–> SubsystemB --InterfaceUsage–> SubsystemA --BindingConnectorAsUsage–> A = C → A

No built-in way, you will need to record the links as you go, and then assemble them the way you want:

links: dict[syside.Element, list[tuple[syside.ConnectorAsUsage, syside.Element]]] = {}

for ...:
    links.setdefault(ends[0].feature_target, []).append(
        (connector, ends[1].feature_target)
    )

Then you can start at some element, and continue following links.

PS. Not all elements have cst_node, e.g. implicit elements inserted during semantic resolution. To get the type name it is better to use type(element).__name__ which works on all elements.