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

Default transition / run-to-completion #31

Open
mikolaj-milewski opened this issue Jan 11, 2018 · 16 comments
Open

Default transition / run-to-completion #31

mikolaj-milewski opened this issue Jan 11, 2018 · 16 comments

Comments

@mikolaj-milewski
Copy link

Is there a way to define default transition (transition without a trigger, which activates automatically on completion of incoming event)? That would allow to run more complex logic in run-to-completion approach.

@ursenzler
Copy link
Member

I don't understand what you try to accomplish.
What do you mean by "completion of incoming event"?

Can you give an example?

@mikolaj-milewski
Copy link
Author

Let's consider state machine:

(o)--->( State_1 )--- Event_1 --->( State_2 )--->( State_3 )

State_1 is initial one. Machine receives Event_1 and transits to State_2. State_2 has an outgoing transition without any event specified as trigger, so it transits to State_3 immediatelly.

@ursenzler
Copy link
Member

This is not how I understood run to completion mode (see https://en.wikipedia.org/wiki/UML_state_machine#Run-to-completion_execution_model)

You could mimic this behaviour by firing a priority event in the entry action. Works only for passive state machines though.

@mikolaj-milewski
Copy link
Author

mikolaj-milewski commented Jan 23, 2018

I know this behavior from IBM Rhapsody which calls it 'null transitions' (www.ibm.com/support/knowledgecenter/SSB2MU_8.1.5/com.ibm.rhp.uml.diagrams.doc/topics/rhp_t_dm_using_null_trans.html). It has a counterpart on UML State Machine called 'completion transition' (http://www-01.ibm.com/support/docview.wss?uid=swg27040251, http://www-sop.inria.fr/members/Charles.Andre/CAdoc/ESINSA/Chap7-StateMachine.pdf).

I've managed to mimic this by sending agreed 'completion event' on extension's method FiredEvent() - works well on passive state machine, haven't tested on active though.

@ursenzler
Copy link
Member

I guess we could add something like this:

machine.In(States.A)
    .Goto(States.B)

Just a Goto without an On that specifies the event.

@mikolaj-milewski
Copy link
Author

It does not compile on 4.4.0 nor 5.0.0-pre0003.

@ursenzler
Copy link
Member

This is an idea. And it is not yet implemented.

@mikolaj-milewski
Copy link
Author

Okay, now I got it. I don't think it will be enough - this transition should have guards and actions as any other.

@ursenzler
Copy link
Member

Yes of course. After the Goto it would be possible to add guards and actions. The addition is that it would be possible to "skip" the On.

I'm really short of time to work on this, maybe you could provide a pull request for this? If you have questions, I can still help.

@mikolaj-milewski
Copy link
Author

Adding If's and Execute's after the Goto seems counterintuitive for me - I'd like to guard the transition itself, not only execution of action. Maybe adding specific function to indicate completion transition would be better? For example:

machine.In('a').Continue().If(() => true).Goto('b')

Regarding If after Goto - I've done some tests and it seems erroneous for me. I'll file another issue.

@ursenzler
Copy link
Member

Yes, you are right, I mixed that up.

The order of the configuration parts is always like this:
machine.In.On.If.Goto.Execute

And I like your idea of using Continue because it makes it much clearer that we immediately forward to another state.

@zorgoz
Copy link

zorgoz commented Jun 4, 2018

Any supported way to achieve this in the current version? I tried creating an extension, but IStateMachineInformation does not expose any way to fire an event.

My workaround is this:

public static class StateMachineExtensions
{
	public static void SetNullTransitions<TState, TEvent>(this IStateMachine<TState, TEvent> machine, TEvent nullTransition, params TState[] states) where TState : IComparable where TEvent : IComparable
	{
		machine.TransitionCompleted += (s, a) => { if (states.Contains(a.NewStateId)) machine.FirePriority(nullTransition); };
	}
}

Of course you still need to define the null transition itself on each state involved.

@ComtelJeremy
Copy link

Hello @ursenzler , are there any plans to implement this? Anything I can do to assist? This seems like a nice solution to a few of my current problems.

@ursenzler
Copy link
Member

@ComtelJeremy I have no plans for this state machine 😱
It is my hobby project and I only add things that I need myself. I simply don't have the time for more.

But in case you provide a Pull Request, I'll gladly answer your questions, review it and merge it if possible.

I also struggle still a bit with this idea. The reason is that the "state" that is immediately left is not a real state (a state that the state machine resides in). However, if the addition of "Continue" has no negative side-effects on the existing functionality, I won't block it.

@ComtelJeremy
Copy link

@ursenzler Thank you for letting me know. Any guidance on where best to put this functionality if I write a PR? I'm just starting to get familiar with your code. Nice work!

As for the concept, perhaps this use case would help you understand my need (or you could point me in a better direction)... I am using a state to clean up resources (dispose them), update the UI, and then loop back to the initial state or to a completed state based on a guard clause. I think that (very briefly entered) state is needed since many other branches use it as their endpoint and not all of them are entered (guard clauses).

I'm currently using this workaround: comment.

@ursenzler
Copy link
Member

Please notice that there are two state machines in the code: the one supporting async and the one without support for async.
I think it is best to first get the async machine running with the new functionality. It is easier to port the changes from the async to the non-async machine than vice-versa.

I would start with writing a spec/test that shows how the new functionality should work.
To make that compile, I'd extend the syntax. Getting the syntax work can sometimes be a bit tricky.
The difficult part is where the logic for the continue should be placed in the state machine. I'm unsure where it would be best.

But here is probably a good candidate that could work:
StateMachine directly after executing a transition (

). The result should probably indicate that we need to "continue".
The information that we should continue can be obtained here:

I hope this gives you a good start!

You can open a PR even before making any changes and with incomplete changes. This makes helping easier because I can comment directly in your code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants