labs4capella / python4capella

Python for Capella
Eclipse Public License 2.0
52 stars 10 forks source link

StateTransition .get_source() and .get_target() always return an empty JavaList #213

Closed chgio closed 2 weeks ago

chgio commented 6 months ago

Hello, firstly thank you very much for this project. I just started using it and I'm very excited, it seems very powerful and well-documented.

I'm trying to build a Finite State Machine parser for usage with formal verification frameworks. Here is some simple code I've been playing around with:

def print_fsm(se_lv):
    fsm_states_lv = se_lv.get_all_contents_by_type(State)

    print("states:")
    for s in fsm_states_lv:
        print(f"\t{s.get_name()}")

        print(f"\t\tincoming transitions:")
        for t in s.get_incoming():
            src = t.get_source()
            tgt = t.get_target()
            print(f"\t\t\t{src}\t---\t{t.get_name()}\t-->\t{tgt}")

        print(f"\t\toutgoing transitions:")
        for t in s.get_outgoing():
            src = t.get_source()
            tgt = t.get_target()
            print(f"\t\t\t{src}\t---\t{t.get_name()}\t-->\t{tgt}")

    print()

This works perfectly to get the States at the se_lv level, and the StateTransitions for each State -- however, when it comes to getting the source and target States for each StateTransition, this is what I get:

states:
    Detumbling
        incoming transitions:
            []  --- Ejection Loop Entry --> []

Indeed, trying to call .get_name() on src or tgt (as done on s and t) gives:

AttributeError: 'JavaList' object has no attribute 'get_name'

I've taken a look at the code and tried substituting capella_query_by_name(t, "Source") (the current implementation of StateTransition.get_source()) with t.get_java_object().getSource() (as done for State.get_incoming()), but to no avail. Starting from se_lv.get_all_contents_by_type(StateTransition) and getting all the source and target States from there also doesn't change anything.

To my understanding, this happens despite the presence of relations between AbstractState and StateTransition entities in the metamodel... I've double-checked my model and the semantic browser does show all the links I need; this issue also seems to extend to multiple levels, as my model has StateMachines in SystemAnalysis, LogicalArchitecture, and PhysicalArchitecture, which all replicate the same behaviour.

ylussaud commented 5 months ago

Hi and thank you for using Python4Capella,

the Python class StateTransition uses capella_query_by_name() for implementing get_source() and get_target(). capella_query_by_name() returns a List... That's why you have this error message:

AttributeError: 'JavaList' object has no attribute 'get_name'

The implementation should directly call the Java method:

    def get_source(self):
        """
        Returns: AbstractState
        """
        value =  self.get_java_object().getSource()
        if value is None:
            return value
        else:
            e_object_class = getattr(sys.modules["__main__"], "EObject")
            specific_cls = e_object_class.get_class(value)
            if specific_cls is None:
                return None
            else:
                return specific_cls(value)
    def get_target(self):
        """
        Returns: AbstractState
        """
        value =  self.get_java_object().getTarget()
        if value is None:
            return value
        else:
            e_object_class = getattr(sys.modules["__main__"], "EObject")
            specific_cls = e_object_class.get_class(value)
            if specific_cls is None:
                return None
            else:
                return specific_cls(value)
chgio commented 5 months ago

Just copy-pasted this in simplified_api/capella.py and it works like a charm!

Now I can get my data:

states:
    Detumbling
        incoming transitions:
            Idle    --- Ejection Loop Entry --> Detumbling

Thank you for the quick and effective bugfix :heart: