statemachine icon indicating copy to clipboard operation
statemachine copied to clipboard

Default transition / run-to-completion

Open mikolaj-milewski opened this issue 7 years ago • 16 comments

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.

mikolaj-milewski avatar Jan 11 '18 11:01 mikolaj-milewski

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

Can you give an example?

ursenzler avatar Jan 12 '18 08:01 ursenzler

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.

mikolaj-milewski avatar Jan 13 '18 12:01 mikolaj-milewski

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.

ursenzler avatar Jan 22 '18 14:01 ursenzler

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.

mikolaj-milewski avatar Jan 23 '18 10:01 mikolaj-milewski

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.

ursenzler avatar Feb 06 '18 15:02 ursenzler

It does not compile on 4.4.0 nor 5.0.0-pre0003.

mikolaj-milewski avatar Feb 06 '18 17:02 mikolaj-milewski

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

ursenzler avatar Mar 07 '18 16:03 ursenzler

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

mikolaj-milewski avatar Mar 07 '18 20:03 mikolaj-milewski

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.

ursenzler avatar Mar 08 '18 08:03 ursenzler

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.

mikolaj-milewski avatar Mar 08 '18 09:03 mikolaj-milewski

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.

ursenzler avatar Mar 09 '18 15:03 ursenzler

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.

zorgoz avatar Jun 04 '18 07:06 zorgoz

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.

ComtelJeremy avatar Oct 27 '20 02:10 ComtelJeremy

@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.

ursenzler avatar Oct 27 '20 11:10 ursenzler

@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.

ComtelJeremy avatar Oct 27 '20 17:10 ComtelJeremy

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 (https://github.com/appccelerate/statemachine/blob/6ae943467fca0472887aefeeeb9d387cb17354f4/source/Appccelerate.StateMachine/AsyncMachine/StateMachine.cs#L153). The result should probably indicate that we need to "continue". The information that we should continue can be obtained here:

  • StateLogic calling the transition https://github.com/appccelerate/statemachine/blob/6ae943467fca0472887aefeeeb9d387cb17354f4/source/Appccelerate.StateMachine/AsyncMachine/States/StateLogic.cs#L64
  • TransitionLogic that knows the actually executed transition and has access to its definition https://github.com/appccelerate/statemachine/blob/6ae943467fca0472887aefeeeb9d387cb17354f4/source/Appccelerate.StateMachine/AsyncMachine/Transitions/TransitionLogic.cs#L95

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.

ursenzler avatar Oct 28 '20 08:10 ursenzler