dc4bc/fsm/fsm_pool/fsm_pool.go

152 lines
3.4 KiB
Go

package fsm_pool
import (
"errors"
"github.com/depools/dc4bc/fsm/fsm"
)
type IStateMachine interface {
// Returns machine state from scope dump
// For nil argument returns fsm with process initiation
// Get() IStateMachine
Name() string
InitialState() string
// Process event
Do(event string, args ...interface{}) (*fsm.FSMResponse, error)
GlobalInitialEvent() string
EventsList() []string
StatesSourcesList() []string
IsFinState(state string) bool
}
type FSMMapper map[string]IStateMachine
type FSMRouteMapper map[string]string
type FSMPoolProvider struct {
fsmInitialEvent string
// Pool mapper by names
mapper FSMMapper
events FSMRouteMapper
states FSMRouteMapper
}
func Init(machines ...IStateMachine) *FSMPoolProvider {
if len(machines) == 0 {
panic("cannot initialize empty pool")
}
p := &FSMPoolProvider{
mapper: make(FSMMapper),
events: make(FSMRouteMapper),
states: make(FSMRouteMapper),
}
allInitStatesMap := make(map[string]string)
// Fill up mapper
for _, machine := range machines {
if machine == nil {
panic("machine not initialized, got nil")
}
machineName := machine.Name()
if machineName == "" {
panic("machine name cannot be empty")
}
if _, exists := p.mapper[machineName]; exists {
panic("duplicate machine name")
}
allInitStatesMap[machine.InitialState()] = machineName
machineEvents := machine.EventsList()
for _, event := range machineEvents {
if _, exists := p.events[event]; exists {
panic("duplicate public event")
}
p.events[event] = machineName
}
// Setup entry event for machines pool if available
if initialEvent := machine.GlobalInitialEvent(); initialEvent != "" {
if p.fsmInitialEvent != "" {
panic("duplicate entry event initialization")
}
p.fsmInitialEvent = initialEvent
}
p.mapper[machineName] = machine
}
// Second iteration, all initial states filled up
// Fill up states with initial and exit states checking
for _, machine := range machines {
machineName := machine.Name()
machineStates := machine.StatesSourcesList()
for _, state := range machineStates {
if machine.IsFinState(state) {
// If state is initial for another machine,
if initMachineName, exists := allInitStatesMap[state]; exists {
p.states[allInitStatesMap[state]] = initMachineName
continue
}
}
if name, exists := p.states[state]; exists && name != machineName {
panic("duplicate state for machines")
}
p.states[state] = machineName
}
}
if p.fsmInitialEvent == "" {
panic("machines pool entry event not set")
}
return p
}
func (p *FSMPoolProvider) EntryPointMachine() (IStateMachine, error) {
// StateGlobalIdle
// TODO: Short code
entryStateMachineName := p.events[p.fsmInitialEvent]
machine, exists := p.mapper[entryStateMachineName]
if !exists || machine == nil {
return nil, errors.New("cannot init machine with entry point")
}
return machine, nil
}
func (p *FSMPoolProvider) MachineByEvent(event string) (IStateMachine, error) {
eventMachineName := p.events[event]
machine, exists := p.mapper[eventMachineName]
if !exists || machine == nil {
return nil, errors.New("cannot init machine for event")
}
return machine, nil
}
func (p *FSMPoolProvider) MachineByState(state string) (IStateMachine, error) {
eventMachineName := p.states[state]
machine, exists := p.mapper[eventMachineName]
if !exists || machine == nil {
return nil, errors.New("cannot init machine for state")
}
return machine, nil
}