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

the sequence of exit actions are not executed correctly #935

Closed
vahdat-ab opened this Issue Dec 16, 2016 · 25 comments

Comments

Projects
None yet
4 participants
@vahdat-ab
Member

vahdat-ab commented Dec 16, 2016

Here is somehow a complete example shows how exit must be treated.
@TimLethbridge @opeyemiAdesina I wonder if you guys can check if the semantics related to execution order is correct or not?

class X{
  sm{
    on{
      exit /{exit_on_execute();}
      e1-> off;
      e2-> on;
      s1{
        exit /{exit_s1_execute();}
        e3-> s2;
        e4-> s1;
        e5-> on;
        e6-> off;
        m1{
          exit /{exit_m1_execute();}
          e7-> m2;
          e8-> m1;
          e9-> s1;
          e10->s2;
          e11->on;
          e12->off;
        } 
        m2{} 
      }
      s2{}
    }
    off{}
  }
  void exit_on_execute(){System.out.println("exited on");}
  void exit_s1_execute(){System.out.println("exited s1");}
  void exit_m1_execute(){System.out.println("exited m1");}
}

The following part shows the execution oders of exits for each event. It shows which is supported currently by Umple.

//e1-> off; : exit_on_execute(); //correct
//e2-> on; : exit_on_execute(); //correct
//e3-> s2; : exit_s1_execute(); //correct
//e4-> s1; : exit_s1_execute(); //correct
//e5-> on; : exit_s1_execute(); -> exit_on_execute(); //Not correct
//e6-> off; : exit_s1_execute(); -> exit_on_execute(); //Not correct
//e7-> m2; : exit_m1_execute(); //correct
//e8-> m1; : exit_m1_execute(); //correct
//e9-> s1; : exit_m1_execute(); ->exit_s1_execute(); //Not correct
//e10->s2; : exit_m1_execute(); ->exit_s1_execute(); //Not correct
//e11->on; : exit_m1_execute(); -> exit_s1_execute(); -> exit_on_execute(); //Not correct
//e12->off; : exit_m1_execute(); -> exit_s1_execute(); -> exit_on_execute(); //Not correct

@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Dec 16, 2016

@opeyemiAdesina
The execution order specified above is logical. it's just for explanation. for e1 it should first exit_on_execute(); and then if there is an entry it must be executed. The generated code doesn't have issues with entry actions. That's the reason I didn't specify it there.

@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Dec 16, 2016

My comments regarding //correct and //Not correct meant to say that Umple supports it or not.
when I say it's correct, it means in the generated code I saw the order and it's fine. Otherwise, generated code doesn't satisfy it.

@TimLethbridge

This comment has been minimized.

Member

TimLethbridge commented Dec 16, 2016

I had put some answers in #766

I am copying here
Here's my take on Vahdat's comments about 'Not correct'

//e5-> on; : exit_s1_execute(); -> exit_on_execute(); //Not correct
It needs to do exit_m1_execute if it is in m1. But if it is in m2 it doesn't
So yes, incorrect, depending on the starting state.

//e6-> off; : exit_s1_execute(); -> exit_on_execute(); //Not correct
Same comment as above,

//e9-> s1; : exit_m1_execute(); ->exit_s1_execute(); //Not correct
I think this is actually correct.

//e10->s2; : exit_m1_execute(); ->exit_s1_execute(); //Not correct
I think this is actually correct

//e11->on; : exit_m1_execute(); -> exit_s1_execute(); -> exit_on_execute(); //Not correct
Seems correct to me

//e12->off; : exit_m1_execute(); -> exit_s1_execute(); -> exit_on_execute(); //Not correct
Seems correct to me

@jblang94 jblang94 self-assigned this Feb 4, 2017

@jblang94

This comment has been minimized.

Contributor

jblang94 commented Feb 11, 2017

@vahdat-ab @TimLethbridge

I read your comments prior to examining the generated code. Below are some questions I have regarding the execution order, and my current understanding. I am wondering if someone could please clarify?

Transitions defined in state "on"

  1. e1 -> off
    Current Execution: exit_on_execute();
    Correct: We are exiting state "on" to go to state "off".

  2. e2 -> on
    Current Execution: exit_on_execute();
    Why is "exit_on_execute()" called even though the transition is not taking us out of state "on"?

Transitions defined in state "s1"

Overall question regarding: e3, e4, e5, e6
For all of these cases, there is no check if "s1" is in state "m1". If "s1" happens to be in state "m1" at the time, shouldn't the execution be exit_m1_execute() and then exit_s1_execute()? Tim noted the same observation above in his comment.

  1. e3 -> s2
    Current Execution: exit_s1_execute();
    Correct (asides from checking for "m1"): We are exiting state "s1" to go to state "s2". "s2" is a state within "on" so we do not call exit_on_execute().

  2. e4 -> s1
    Current Execution: exit_s1_execute();
    Why is "exit_s1_execute()" called even though the transition is not taking us out of state "s1"?

  3. e5 -> on
    Current Execution: exit_s1_execute()
    Correct (asides from checking for "m1"): exit_on_execute() would not be called because "s1" is a state defined in "on", and we are not exiting "on".

  4. e6 -> off
    Current Execution: exit_s1_execute()
    Incorrect: We are missing a call to exit_on_execute(). In addition to exiting state "s1", we are also exiting state "on".

Transitions defined in state "m1"

  1. e7 -> m2
    Current Execution: exit_m1_execute()
    Correct: We are exiting "m1" to go to state "m2". "m2" is a state defined in "s1".

  2. e8 -> m1
    Current Execution: exit_m1_execute()
    Why is "exit_m1_execute()" called even though the transition is not taking us out of state "m1"?

  3. e9 -> s1
    Current Execution: exit_m1_execute()
    Correct: We are exiting "m1" and not exiting state "s1", hence exit_s1_execute() is not called.

  4. e10 -> s2
    Current Execution: exit_m1_execute()
    Incorrect: We are exiting "s1" as well. The execution should be exit_m1_execute() -> exit_s1_execute().

  5. e11 -> on
    Current Execution: exit_m1_execute()
    Incorrect: Same comment as e10

  6. e12 -> off
    Current Execution: exit_m1_execute()
    Incorrect: We are exiting states "s1", and "on" as well. The execution should be exit_m1_execute() -> exit_s1_execute() -> exit_on_execute().

@jblang94

This comment has been minimized.

Contributor

jblang94 commented Feb 13, 2017

I noticed today that the PHP and Java code generation differ for some events. For instance, in e2, the PHP code does not call "$this->exitSm()", which in turn calls "exit_on_execute()". In the Java code, e2 calls "exitSm()" which in turn calls "exit_on_execute()".

For e1, both PHP and Java. Is PHP supposed to differ from Java for generated event methods?

Java Generated Code for e2
public boolean e2()
  {
    boolean wasEventProcessed = false;
    
    Sm aSm = sm;
    switch (aSm)
    {
      case on:
        exitSm();
        setSm(Sm.on);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }
PHP Generated Code for e2
public function e2()
  {
    $wasEventProcessed = false;
    
    $aSm = $this->sm;
    if ($aSm == self::$SmOn)
    {
      $this->setSm(self::$SmOn);
      $wasEventProcessed = true;
    }
    return $wasEventProcessed;
  }
@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Feb 13, 2017

e2 -> on
Current Execution: exit_on_execute();
Why is "exit_on_execute()" called even though the transition is not taking us out of state "on"?

There are two concepts related to this situation. They are called internal and external transitions. In internal transitions, exit actions are executed, but in the external transitions, they don't. Umple accepted just the external transitions. That's the reason we run that method.

shouldn't the execution be exit_m1_execute() and then exit_s1_execute()? Tim noted the same observation above in his comment.

Correct.

e4 -> s1
Current Execution: exit_s1_execute();
Why is "exit_s1_execute()" called even though the transition is not taking us out of state "s1"?

The reason was described above.

e5 -> on
Current Execution: exit_s1_execute()
Correct (asides from checking for "m1"): exit_on_execute() would not be called because "s1" is a state defined in "on", and we are not exiting "on".

No, asides from checking for m1, this sequence must be executed exit_s1_execute(); -> exit_on_execute()

e8 -> m1
Current Execution: exit_m1_execute()
Why is "exit_m1_execute()" called even though the transition is not taking us out of state "m1"?

The reason was described above.

e9 -> s1
Current Execution: exit_m1_execute()
Correct: We are exiting "m1" and not exiting state "s1", hence exit_s1_execute() is not called.

We need exit_s1_execute() because e9 is an external transition.

e11 -> on
Current Execution: exit_m1_execute()
Incorrect: Same comment as e10

The sequence is exit_m1_execute(); -> exit_s1_execute(); -> exit_on_execute()

All of the transitions in Umple must be treated as external transitions. Most of your comments were about internal transitions.

@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Feb 13, 2017

I noticed today that the PHP and Java code generation differ for some events. For instance, in e2, the PHP code does not call "$this->exitSm()", which in turn calls "exit_on_execute()". In the Java code, e2 calls "exitSm()" which in turn calls "exit_on_execute()".

The most valid semantics is the one you see in Java. PHP code generator might not be up to date. The way PHP is dealing might be different, but the sequences of actions must be the same for all target languages.

@jblang94

This comment has been minimized.

Contributor

jblang94 commented Feb 14, 2017

@vahdat-ab Thank you for your feedback. I have a couple of follow up clarifications/questions below.

In internal transitions, exit actions are executed, but in the external transitions, they don't.

I think you meant that "In external transitions, exit actions are executed, but in the internal transitions, they don't." ?

Also for e1 and e2, I think we should also check if "on" is in state "s1", and if "s1" is in state "m1". Potentially, for both of these transitions, the execution could be
exit_m1_execute() -> exit_s1_execute() -> exit_on_execute()
Is this correct? I made this statement based on my understanding of external transitions, and from following the logic for e3 to e6.

My plan now is to first tackle the Java code generation. I believe my changes will be going in UmpleToJava's "state_machine_Event.ump". After getting the Java code generation to work, I will then transfer the fix into UmpleToPHP's "state_machine_Event.ump".

@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Feb 14, 2017

@jblang94

I think you meant that "In external transitions, exit actions are executed, but in the internal transitions, they don't." ?

Yes. my bad.

Is this correct? I made this statement based on my understanding of external transitions, and from following the logic for e3 to e6.

Yes. This is called hierarchical state. It means it doesn't matter in which internal state the system is, once an event comes in the top state, the internal states must be left. This is also applied to parallel regions as well.

My plan now is to first tackle the Java code generation.

Yes. Work on Java

@jblang94

This comment has been minimized.

Contributor

jblang94 commented Feb 28, 2017

@vahdat-ab I've removed my comment from yesterday, and have instead created two gists that contain possible approaches for fixing this issue. If you could please let me know which one you prefer, or if there is another way the code generation should happen that would be great.

Approach 1: https://gist.github.com/jblang94/f96cd9eb8ba0a5323328f6adfeb429ca
Approach 2: https://gist.github.com/jblang94/96e4a7e8a53b64b3472771e5b8e2cc71

@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Mar 1, 2017

Approach one is not good because it's not modular and so forget it.
approach two can be improved. You need to make sure your approach is modular.
Please consider this and let me know if it make sense or not:

class  A{
  sm{
    on{
      exit /{eOn1();}
      e1-> off;
      m1{
          exit/{eM1();}
          e20-> m2;
          t2{
            exit /{eT1();}
            e3-> t3;
          }
          t3{}
      }
      m2{}    
    }  
    off{
      exit/{exitOff();}      
    }
  }
}

I suggest the following modified three exit methods

  private void exitSm()  //I modified
  {
    switch(sm)
    {
      case on:
        exitSmOn(); This is used instead of exitOn(); I think we don't need exitOn() (need more investigation)
        eOn1();
        break;
      case off:
        exitOff();
        break;
    }
  }

  private void exitSmOn() //I modified
  {
    switch(smOn)
    {
      case m1:
        exitSmOnM1(); //This is used instead of exitOn(); I think we don't need exitOn() (need more investigation)
        eM1();
        break;
    }
  }  
  
  private void exitSmOnM1() //correct
  {
    switch(smOnM1)
    {
      case t2:
        eT1();
        break;
    }
  }

I don't understand why we have private boolean exitOn(). Please investigate several examples and let me know what's wrong with my suggestion. To me, the current implementation has duplication, except if it does sth that I cannot understand or I miss.
T me if we have three state machines we should have three exit methods and not four.

@jblang94

This comment has been minimized.

Contributor

jblang94 commented Mar 2, 2017

@vahdat-ab I agree with the modular approach, however, we still need the setter calls in exitSmOn, and exitSmOnM1. We would also need the other states within the state machine that don't have exit actions so that we can set the state machine variable correctly. I also agree that we shouldn't be generating the "exitOn" method

private void exitSm()  // No changes from your modifications
  {
    switch(sm)
    {
      case on:
        exitSmOn();
        eOn1();
        break;
      case off:
        exitOff();
        break;
    }
  }

  private void exitSmOn()
  {
    switch(smOn)
    {
      case m1:
        exitSmOnM1(); // Kept from your modification
        eM1();
        setSmOn(SmOn.Null); // Add call to the setter method
        break;
     case m2:
        setSmOn(SmOn.Null); // Add call to the setter method
        break;
    }
  }  
  
  private void exitSmOnM1()
  {
    switch(smOnM1)
    {
      case t2:
        eT1();
        setSmOnM1(SmOnM1.Null); // Add call to the setter method
        break;
      case t3:
        setSmOnM1(SmOnM1.Null); // Add call to the setter method
        break;
    }
  }

As for exitOn(), it appears that it accounts for all state machines within the state "on". I have a sneaking suspicion that "public StateMachine exitableStateMachine(State nextState)" and "public StateMachine exitableSelfTransition(State nextState)" in StateMachine_Code.ump are going to have to be modified. I will investigate why exitOn is being generated. Should I also be looking in UmpleInternalParser since that's where exit and entry actions are first processed?

Also, when there are no exit actions declared, should we still use the modular approach as we've discussed (remove the exit action calls in exitSm, exitSmOn, exitSmOnM1)? When there are no exit actions, exitOn is still generated:

private boolean exitOn()
{
    boolean wasEventProcessed = false;
    
    SmOn aSmOn = smOn;
    SmOnM1 aSmOnM1 = smOnM1;
    switch (aSmOn)
    {
      case m1:
        setSmOn(SmOn.Null);
        wasEventProcessed = true;
        break;
      case m2:
        setSmOn(SmOn.Null);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    switch (aSmOnM1)
    {
      case t2:
        setSmOnM1(SmOnM1.Null);
        wasEventProcessed = true;
        break;
      case t3:
        setSmOnM1(SmOnM1.Null);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }
}
@jblang94

This comment has been minimized.

Contributor

jblang94 commented Mar 2, 2017

I believe I have identified why "exitOn" is being generated. For each of the states "m1", "m2", "t2", and "t3", "exitOn" is included in their transitions. I have a feeling it's because the compiler is adding a transition for when a state machine transitions to Null. The "exitOn" method is generated by the code in "state_machine_Event.ump", whereas the "exitSm", "exitSmOn", and "exitSmOnM1" methods are generated by "state_machine_Set.ump". I will continue my investigation in UmpleInternalParser.

@jblang94

This comment has been minimized.

Contributor

jblang94 commented Mar 3, 2017

Upon further investigation, I found that the "exitOn" transition is not being added by UmpleInternalParser, but it is created in JavaGenerator.java . The "exitOn" transition is created in the "prepareNestedStatesFor" method, and then it is added to the states in "GeneratorHelper.prepareNestedStateMachine".

I have a feeling it's because the compiler is adding a transition for when a state machine transitions to Null.

While it is actually the JavaGenerator adding the "exitOn" transition, it is adding it to strictly support a state machine's transition from a non-Null state to the Null state.

@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Mar 3, 2017

however, we still need the setter calls in exitSmOn, and exitSmOnM1.

Yours is complete.

I will investigate why exitOn is being generated. Should I also be looking in UmpleInternalParser since that's where exit and entry actions are first processed? Also, when there are no exit actions declared, should we still use the modular approach as we

You don't need. nothing serious happens at the parsing level

Also, when there are no exit actions declared, should we still use the modular approach as we've discussed (remove the exit action calls in exitSm, exitSmOn, exitSmOnM1)?

To me yes, because we still need to set states to be null

While it is actually the JavaGenerator adding the "exitOn" transition, it is adding it to strictly support a state machine's transition from a non-Null state to the Null state.

can you come up with an example?

Another point is that they way exitOn is named is suspicious to a name conflict in a big state machine. I hope it's sth we really don't need.

@jblang94

This comment has been minimized.

Contributor

jblang94 commented Mar 5, 2017

@vahdat-ab I am not sure of what example to provide, but here is a description of why "exitOn" is created, and why I think that we don't need it. Also, as a result of my investigation today, I have identified how I can modify the "prepareNestedStatesFor" method in JavaGenerator, and the "prepareNestedStateMachine" method in GeneratorHelper. Note, both of these methods are called before we begin generating the Java code.

In prepareNestedStateMachine, "exitOn" is created as an internal transition and is added to each state in the state machine.

      // From prepareNestedStateMachine in GeneratorHelper.java
      for (State state : sm.getStates())
      {
        if (state == nullState) { continue; }
        Transition exitTransition = state.addTransition(nullState,0);
        exitTransition.setIsInternal(true);
        exitTransition.setEvent(exitEvent);    // exitEvent is "exitOn"
      }

When code generation occurs, the states "m1", "m2", "t2", "t3", all have exit transitions with the same event name "exitOn". This is why both "SmOn" and "SmOnM1" transition to Null in the same method. Overall, it seems like the "exitOn" method is created to provide a way to set all of the nested state machines within a state to Null. Essentially, it is used to try to implement "external transitions". Modularizing the exit methods will allow us to not need "exitOn" because this will take care of switching the state machines to their Null states.

The "exitStateMachine" (i.e. exitSmOn) methods are created when exit actions are present. Another step that takes place in "prepareNestedStatesFor" is that "exitOn" is added as an exit action for both states "on" and "m1". States "m2", and "t3" are missing within "exitSmOn" and "exitSmOnM1" respectively because they do not have exit actions.

Also, "exitOn" gets its name because of the following code in "prepareNestedStatesFor"

while(parentState.getStateMachine().getParentState() != null)
{
    parentState = parentState.getStateMachine().getParentState();
}
Map<String,String> lookups = new HashMap<String,String>();
lookups.put("exitEventName",translate("exitMethod",parentState));

What I am proposing to do is the following:

  1. Remove the creation of the "exitOn" transition and exit action in "prepareNestedStatesFor" and "prepareNestedStateMachine"
  2. When iterating over each state in "prepareNestedStateMachine"
    1. For each nested state machine in a state
      1. Add an exit action which will call the corresponding "exitNestedStateMachine" method
    2. Add an exit action which will call the state machine's setter function to switch it to the Null state.

When the "exitStateMachine" methods are generated, each case will look like the following:

case state: 
      // Calls to exit nested state machines in state (i.e. exitSmOnM1())
      // Any user defined exit action (i.e. eM1())
      // Set the state machine's state to Null (i.e. setSmOn(SmOn.Null))

I will also have to update the event generation code so that events call the super state exit method (I am not sure if this is the proper terminology). In our above example, events would call "exitSm".

@opeyemiAdesina

This comment has been minimized.

Contributor

opeyemiAdesina commented Mar 5, 2017

@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Mar 5, 2017

When the "exitStateMachine" methods are generated, each case will look like the following:

To me it makes sense. You need to check the failed test cases and compare the outputs carefully. If you see some extra output in those test cases that you approach doesn't have let me know.

@jblang94

This comment has been minimized.

Contributor

jblang94 commented Mar 8, 2017

@vahdat-ab consider the following example with concurrent state machines

class X {
  sm {
    s1 {
      a {
        exit/{A_exit_action();}
        goToS2 -> s2;
      }
      ||
      b {
        exit/{B_exit_action();}
        goToA -> a;
      }
    }
    s2 {}
  }
}

Currently, my change generates this, in terms of exit methods.

public boolean goToA()
{
    boolean wasEventProcessed = false;
    
    SmS1B aSmS1B = smS1B;
    switch (aSmS1B)
    {
      case b:
        exitSm();
        setSmS1A(SmS1A.a);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
}

private void exitSm()
{
    switch(sm)
    {
      case s1:
        exitSmS1B();
        exitSmS1A();
        break;
    }
}

private void exitSmS1A()
{
    switch(smS1A)
    {
      case a:
        A_exit_action();
        setSmS1A(SmS1A.Null);
        break;
    }
}

private void exitSmS1B()
{
    switch(smS1B)
    {
      case b:
        B_exit_action();
        setSmS1B(SmS1B.Null);
        break;
    }
}

I think that the generated code is wrong because a transition that occurs in "A" should not affect the state of "B". Could you please clarify what the expected behaviour is for parallel state machines?

@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Mar 9, 2017

Although we do not prioritize the way parallel state machines exit it's better to follow the rule we have in Umple. If it comes first in the code, it runs first.

private void exitSm()
{
    switch(sm)
    {
      case s1:
        exitSmS1A();
        exitSmS1B();
        break;
    }
}

Regarding the event goToA, once it is executed, the state machine smS1A must be in state a and the state machine smS1B must be in b. The reason for b is this: it is the initial state of the state machine smS1B. I cannot see it happens in the above code. it's set to null.
@opeyemiAdesina you might want to check as well.

@jblang94

This comment has been minimized.

Contributor

jblang94 commented Mar 9, 2017

@vahdat-ab sorry, I made a mistake in my initial question. I think that goToA is actually correct since we would be leaving parallel state machine "b" to go to "a". Hence SmS1B would be set to Null after this transition. However, with the other transition in my example, "goToS2", the following is generated:

public boolean goToS2()
  {
    boolean wasEventProcessed = false;
   
    SmS1A aSmS1A = smS1A;
    switch (aSmS1A)
    {
      case a:
        exitSm();
        setSm(Sm.s2);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }

Is it correct that the code exits both parallel state machines "a" and "b"? My understanding is that since the transition is a part of parallel state machine "a", we should be exiting this state machine, and SmS1A should be set to Null. We should not be exiting the parallel state machine "b", and its state should not be affected by "goToS2".

@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Mar 9, 2017

I think that goToA is actually correct since we would be leaving parallel state machine "b" to go to "a". Hence SmS1B would be set to Null after this transition

No, this shouldn't happen. This is called and-cross transition and you need to set all other parallel state machines inside state machine s1 to their initial states except state machine a (which will be state a). They all are null when the state machine s is not in state s1.
In this sample, it happened to be the same because the initial state and destination state of the transition are the same.

@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Mar 9, 2017

Is it correct that the code exits both parallel state machines "a" and "b"?

Yes, that's correct, both.

My understanding is that since the transition is a part of parallel state machine "a", we should be exiting this state machine, and SmS1A should be set to Null. We should not be exiting the parallel state machine "b", and its state should not be affected by "goToS2".

No, it should leave all state machines inside state s and set them to null. This comes from the concept of inheritance in state machines.

@jblang94

This comment has been minimized.

Contributor

jblang94 commented Mar 10, 2017

@vahdat-ab thanks for the clarifications. Before I modify my fix, I'd like to confirm my understanding for how to handle the exit methods for parallel state machines:

class X {
  sm {
    s1 {
      a {
        exit/{exit_a_action();}
        t1 {
          goToT2 -> t2;
        }
        t2 {
          goToT4 -> t4;
        }
      }
      ||
      b {
        exit/{exit_b_action();}
        t3 { }
        t4 {
          goToS2 -> s2;
        }
      }
    }
    s2 { }
  }
}

Case 1: Transitions between states in the same parallel state machine (i.e. goToT2)

  • parallel state machine "a" should be in state "t2", and parallel state machine "b" should be in its initial state "t3" after calling the exitSm() method
public boolean goToT2()
  {
    boolean wasEventProcessed = false;
    
    SmS1AA aSmS1AA = smS1AA;
    switch (aSmS1AA)
    {
      case t1:
        exitSm();
        setSmS1AA(SmS1AA.t2);
	setSmS1BB(SmS1BB.t3);     // ensure "and-cross"
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }

Case 2: Transition between states in different parallel state machines (i.e. goToT4)

  • parallel state machine "a" should be in its initial state "t1", and parallel state machine "b" should be in state "t4" after executing the exitSm() method
public boolean goToT4()
  {
    boolean wasEventProcessed = false;
    
    SmS1AA aSmS1AA = smS1AA;
    switch (aSmS1AA)
    {
      case t2:
        exitSm();
        setSmS1BB(SmS1BB.t4);
        setSmS1AA(SmS1AA.t1);   // ensure "and-cross"
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }

Case 3: Transition from a parallel state machine's state to a state outside its parent state (i.e. goToS2)

  • parallel state machine "a" and parallel state machine "b" should be set to Null after executing the exitSm() method
public boolean goToS2()
  {
    boolean wasEventProcessed = false;
    
    SmS1BB aSmS1BB = smS1BB;
    switch (aSmS1BB)
    {
      case t4:
        exitSm();          // sets "a" and "b" to Null
        setSm(Sm.s2);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }
@vahdat-ab

This comment has been minimized.

Member

vahdat-ab commented Mar 10, 2017

parallel state machine "a" should be in state "t2", and parallel state machine "b" should be in its initial state "t3" after calling the exitSm() method

No, parallel state machine a should be in state t2, and parallel state machine b should be in any state it is at the execution time (it means no change to it). It might be "t3" or "t4". This is different than the previous case because goToT2 doesn't leave SM a. In this case, there is no need for calling exitSm() because it is still in state s1. There is no and-cross here.

Case 2: Transition between states in different parallel state machines (i.e. goToT4)

Now, this is an and-cross transition. Yes, this is correct.

Case 3: Transition from a parallel state machine's state to a state outside its parent state (i.e. goToS2)

Yup, correct. Good job :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment