go_fsm

package module
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2019 License: MIT Imports: 4 Imported by: 0

README

go-fsm

Finite State Machine in Go (with Blackjack and... using context)

Overview

Finite State Machine is designed with alignment to Erlang Finite State Machine principles and inspired by https://github.com/dyrkin/fsm solution. But has a bit different approach which based on context.Context GO package.

A FSM can be described as a set of relations of the form:

State(S) x Event(E) -> Actions (A), State(S')

If we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S'.

Install

go get -u github.com/igorrius/go-fsm

Example

package main

import (
	"context"
	go_fsm "github.com/igorrius/go-fsm"
	"log"
)

type Logger struct {
}

func (l Logger) Log(v ...interface{}) {
	log.Print(v...)
}

func (l Logger) Logf(format string, v ...interface{}) {
	log.Printf(format, v...)
}

func main() {
	const (
		// define states
		stateIdle     = "idle"
		stateInAction = "inAction"
	)

	// create logger
	logger := &Logger{}

	// create a new instance of FSM,
	// if a logger is not needed use constructor without a logger option e.g. fsm, err := go_fsm.NewFsm().
	fsm, err := go_fsm.NewFsm(go_fsm.LoggerOption(logger)).
		When(
			stateIdle,
			func(eventCtx go_fsm.EventContext, fsmCtx go_fsm.FsmContext) (next go_fsm.State, nextFsmCtx go_fsm.FsmContext, err error) {
				var state go_fsm.State
				var event go_fsm.Event
				// get current state from eventCtx
				if event, err = go_fsm.EventFromCtx(eventCtx); err != nil {
					return
				}

				// get current state from fsmContext
				if state, err = go_fsm.StateFromCtx(fsmCtx); err != nil {
					return
				}

				switch event {
				case "moveRight":
					log.Println("Action: move right")
					nextFsmCtx = context.WithValue(fsmCtx, "degrees", "30")
					next = stateInAction
				case "moveLeft":
					log.Println("Action: move left")
					nextFsmCtx = context.WithValue(fsmCtx, "degrees", "50")
					next = stateInAction
				case "letsError":
					log.Println("Action: letsError")
					next = "unknownState"
				default:
					// FSM must stay in current state
					log.Println("Unknown event: ", event)
					next = state
				}

				return
			},
		).
		When(
			stateInAction,
			func(eventCtx go_fsm.EventContext, fsmCtx go_fsm.FsmContext) (next go_fsm.State, nextFsmCtx go_fsm.FsmContext, err error) {
				var state go_fsm.State
				var event go_fsm.Event
				// get current state from eventCtx
				if event, err = go_fsm.EventFromCtx(eventCtx); err != nil {
					return
				}

				// get current state from fsmContext
				if state, err = go_fsm.StateFromCtx(fsmCtx); err != nil {
					return
				}

				switch event {
				case "stop":
					log.Println("Action: stop")
					log.Println("Degrees: ", fsmCtx.Value("degrees").(string))
					next = stateIdle
				default:
					// FSM must stay in current state
					log.Println("Unknown event: ", event)
					next = state
				}

				return
			},
		).
		RegisterPostTransitionFunc("*", "*",
			func(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {
				log.Println("Transition Function [ANY to ANY]")
				return nil
			},
		).
		RegisterPostTransitionFunc(stateIdle, "*",
			func(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {
				log.Println("Transition Function [stateIdle to ANY]")
				return nil
			},
		).
		RegisterPostTransitionFunc(stateInAction, "*",
			func(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {
				log.Println("Transition Function [stateInAction to ANY]")
				return nil
			},
		).
		RegisterPostTransitionFunc("*", stateInAction,
			func(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {
				log.Println("Transition Function [ANY to stateInAction]")
				return nil
			},
		).
		RegisterPostTransitionFunc("*", stateIdle,
			func(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {
				log.Println("Transition Function [ANY to stateIdle]")
				return nil
			},
		).
		RegisterPostTransitionFunc(stateInAction, stateIdle,
			func(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {
				log.Println("Transition Function [stateIdle to stateInAction]")
				return nil
			},
		).
		RegisterPostTransitionFunc(stateIdle, stateInAction,
			func(from, to go_fsm.State, fsmCtx go_fsm.FsmContext) error {
				log.Println("Transition Function [stateIdle to stateInAction]")
				return nil
			},
		).
		InitWithState(stateIdle)

	if err != nil {
		log.Fatalln("FSM init error:", err)
	}
	defer fsm.Close()

	// call stop - wrong event
	if err = fsm.ProcessEvent("stop", context.TODO()); err != nil {
		log.Fatalln(err)
	}

	// call letsError - error next state
	if err = fsm.ProcessEvent("letsError", context.TODO()); err != nil {
		log.Println("This will be an error:", err)
	}

	// call move right event
	if err = fsm.ProcessEvent("moveRight", context.TODO()); err != nil {
		log.Fatalln(err)
	}

	// call move stop
	if err = fsm.ProcessEvent("stop", context.TODO()); err != nil {
		log.Fatalln(err)
	}

	// call move right event
	if err = fsm.ProcessEvent("moveLeft", context.TODO()); err != nil {
		log.Fatalln(err)
	}

	// call move stop - wrong event
	if err = fsm.ProcessEvent("stop", context.TODO()); err != nil {
		log.Println("This will be an error:", err)
	}
}

Benchmark

Transition_Permitted-8                                   	   10000	    129269 ns/op	     176 B/op	       7 allocs/op
Transition_Denied-8                                        	 2450037	       470 ns/op	     192 B/op	       8 allocs/op
Transition_with_post_transition_functions_run-8         	   10000	    131176 ns/op	     176 B/op	       7 allocs/op

made with love in GO :)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrActionNotFound     = errors.New("action not found")
	ErrCanNotExtractEvent = errors.New("can't extract event from context")
	ErrCanNotExtractState = errors.New("can't extract state from context")
)

Functions

This section is empty.

Types

type ActionFunc

type ActionFunc = func(eventCtx EventContext, fsmCtx FsmContext) (next State, nextFsmCtx FsmContext, err error)

Action function is using to describe behavior within transition to the next state and choose what exactly state is needed arguments: eventCtx - context which received with event fsmCtx - global FSM context which transferring between FSM states results: next - the next state where to FSM try to arrive nextFsmCtx - context which will be used in next state err - possible error which can be raised by action handler

type Event

type Event = string

func EventFromCtx

func EventFromCtx(ctx context.Context) (Event, error)

type EventContext

type EventContext context.Context

type Fsm

type Fsm struct {
	// contains filtered or unexported fields
}

func NewFsm

func NewFsm(opts ...Option) *Fsm

NewFsm create a new instance of FSM if logger was not need should set it to nil

func (*Fsm) Close

func (fsm *Fsm) Close()

close main context and stop all events processing (a try to process event always return an error)

func (Fsm) CurrentState

func (fsm Fsm) CurrentState() State

CurrentState return FSM current state

func (*Fsm) InitWithState

func (fsm *Fsm) InitWithState(state State) (*Fsm, error)

InitWithState init FSM with initial state

func (*Fsm) ProcessEvent

func (fsm *Fsm) ProcessEvent(event Event, eventCtx EventContext) error

Process event by current state action function

func (*Fsm) RegisterPostTransitionFunc

func (fsm *Fsm) RegisterPostTransitionFunc(fromState, toState State, fn TransitionFunc) *Fsm

RegisterPostTransitionFunc add a transition function

func (*Fsm) Reset

func (fsm *Fsm) Reset() error

reset FSM state to initial state and initial context

func (*Fsm) When

func (fsm *Fsm) When(state State, action ActionFunc) *Fsm

When FSM event configuration

type FsmContext

type FsmContext context.Context

type Logger

type Logger interface {
	Log(v ...interface{})
	Logf(format string, v ...interface{})
}

Logger is a generic logging interface

type Option added in v1.1.0

type Option func(*Options)

func LoggerOption added in v1.1.0

func LoggerOption(l Logger) Option

type Options added in v1.1.0

type Options struct {
	Logger Logger
}

type State

type State = string

func StateFromCtx

func StateFromCtx(ctx context.Context) (State, error)

type TransitionFunc

type TransitionFunc = func(from, to State, fsmCtx FsmContext) error

transition function is using to add an additional behavior after transition to the next state

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL