qmuntal / stateless

Go library for creating finite state machines
BSD 2-Clause "Simplified" License
942 stars 49 forks source link

Q: how could I use with `stateless` in my case? #14

Closed yeqown closed 3 years ago

yeqown commented 3 years ago

As the title describes, I'm asking for advice that can help me. My state machine looks like this: image

With my thought of stateless, the state could not contain any variable? just like this:

type state struct {
  s OnOrOffState // indicates which state is activate 
  seq int               // to mark the order of event
}

I must explain why the state machine looks maybe weird:

  1. The event(trigger) could not be ordered all the time, but I need to make sure the final state is correct.
  2. The seq is the key to help me keep the state correct. Maybe there is another way to design the state machine, but for now, I need this seq variable. A new state machine design could be grateful.
  3. Drop event means the action to change state, but I didn't find a clue to define this in stateless
  4. Trigger could be defined as the actual way to change the state, however stateless is not designed like this, right?

Finally, I wonder could stateless help me in this case? Is there any advice on my state machine? it must be better that this issue could help stateless to add new features.

yeqown commented 3 years ago

oops, I find clues by reading code, then I write the code as following:

func prepareStateMachine(gSeq uint8) *stateless.StateMachine {
    sm := stateless.NewStateMachine(_OFFLINE)

    sm.SetTriggerParameters(_ENTER, reflect.TypeOf(emptySeq))
    sm.SetTriggerParameters(_LEAVE, reflect.TypeOf(emptySeq))
    sm.Configure(_OFFLINE).
        PermitDynamic(_ENTER, func(ctx context.Context, i ...interface{}) (stateless.State, error) {
            seq := i[0].(uint8)
            fmt.Printf("[off] enter seq=%d\n", seq)
            if seq <= gSeq {
                return _OFFLINE, nil
            }

            gSeq = seq
            return _ONLINE, nil
        }).
        PermitDynamic(_LEAVE, func(ctx context.Context, i ...interface{}) (stateless.State, error) {
            seq := i[0].(uint8)
            fmt.Printf("[off] leave seq=%d\n", seq)
            if seq <= gSeq {
                return _OFFLINE, nil
            }

            gSeq = seq
            return _OFFLINE, nil
        })

    sm.Configure(_ONLINE).
        PermitDynamic(_ENTER, func(ctx context.Context, i ...interface{}) (stateless.State, error) {
            seq := i[0].(uint8)
            fmt.Printf("[on] enter seq=%d\n", seq)
            if seq <= gSeq {
                return _ONLINE, nil
            }

            gSeq = seq
            return _ONLINE, nil
        }).
        PermitDynamic(_LEAVE, func(ctx context.Context, i ...interface{}) (stateless.State, error) {
            seq := i[0].(uint8)
            fmt.Printf("[on] leave seq=%d\n", seq)
            if seq < gSeq {
                return _ONLINE, nil
            }
            gSeq = seq
            return _OFFLINE, nil
        })

    return sm
}

It seems I have solved this ... :)