diff options
| author | jwijenbergh <jeroenwijenbergh@protonmail.com> | 2022-04-14 13:57:55 +0200 |
|---|---|---|
| committer | jwijenbergh <jeroenwijenbergh@protonmail.com> | 2022-04-14 13:57:55 +0200 |
| commit | 667508f25afda611e64ac8423d6ef0108fb51cec (patch) | |
| tree | 06840ad79187fe95f00ef22267ef2434142a865b /src/fsm.go | |
| parent | 680067ed6154bebcf2c097d407b030b3f786d10c (diff) | |
Simplify FSM by removing hierarchy
Diffstat (limited to 'src/fsm.go')
| -rw-r--r-- | src/fsm.go | 261 |
1 files changed, 52 insertions, 209 deletions
@@ -1,7 +1,6 @@ package eduvpn import ( - "errors" "fmt" "os" ) @@ -9,159 +8,66 @@ import ( type FSMStateID int8 const ( - // Registered means the app is registered with the wrapper - APP_REGISTERED FSMStateID = iota - // Deregistered means the app is not registered with the wrapper - APP_DEREGISTERED + DEREGISTERED FSMStateID = iota + + // No Server means the user has not chosen a server yet + NO_SERVER - // We have the states where a server is chosen or not - // When no server is chosen, we have no substate - CONFIG_NOSERVER + // Chosen Server means the user has chosen a server to connect to + CHOSEN_SERVER - // When a server is chosen we have the remaining states as substates - CONFIG_CHOSENSERVER + // OAuth Started means the OAuth process has started + OAUTH_STARTED - // The states for when the server is authenticated - // The SERVER_AUTHENTICATED is the parent state - // While SERVER_CONNECTED and SERVER_DISCONNECTED are substatse - SERVER_AUTHENTICATED - SERVER_CONNECTED - SERVER_DISCONNECTED + // Authenticated means the OAuth process has finished and the user is now authenticated with the server + AUTHENTICATED - // The states for when the server is not authenticated - // The SERVER_NOT_AUTHENTICATED is the parent state - // While SERVER_INITIALIZED, SERVER_OAUTH_STARTED and SERVER_OAUTH_FINISHED are substates - SERVER_NOT_AUTHENTICATED - SERVER_INITIALIZED - SERVER_OAUTH_STARTED - SERVER_OAUTH_FINISHED + // Connected means the user has been connected to the server + CONNECTED ) func (s FSMStateID) String() string { switch s { - case APP_REGISTERED: - return "APP_REGISTERED" - case APP_DEREGISTERED: - return "APP_DEREGISTERED" - case CONFIG_NOSERVER: - return "CONFIG_NOSERVER" - case CONFIG_CHOSENSERVER: - return "CONFIG_CHOSENSERVER" - case SERVER_AUTHENTICATED: - return "SERVER_AUTHENTICATED" - case SERVER_CONNECTED: - return "SERVER_CONNECTED" - case SERVER_DISCONNECTED: - return "SERVER_DISCONNECTED" - case SERVER_NOT_AUTHENTICATED: - return "SERVER_NOT_AUTHENTICATED" - case SERVER_INITIALIZED: - return "SERVER_INITIALIZED" - case SERVER_OAUTH_STARTED: - return "SERVER_OAUTH_STARTED" - case SERVER_OAUTH_FINISHED: - return "SERVER_OAUTH_FINISHED" + case DEREGISTERED: + return "Deregistered" + case NO_SERVER: + return "No_Server" + case CHOSEN_SERVER: + return "Chosen_Server" + case OAUTH_STARTED: + return "OAuth_Started" + case AUTHENTICATED: + return "Authenticated" + case CONNECTED: + return "Connected" default: panic("unknown conversion of state to string") } } type ( - FSMStates map[FSMStateID]*FSMState FSMTransitions []FSMStateID + FSMStates map[FSMStateID]FSMTransitions ) -type FSMState struct { - Sub *FSM - Transition FSMTransitions - - // When Locked=True it cannot go to the parent state and transition away - Locked bool -} - type FSM struct { States FSMStates Current FSMStateID } -func (fsmState *FSMState) hasTransition(check FSMStateID) bool { - for _, state := range fsmState.Transition { - if state == check { +func (eduvpn *VPNState) HasTransition(check FSMStateID) bool { + for _, transition_state := range eduvpn.FSM.States[eduvpn.FSM.Current] { + if transition_state == check { return true } } - return false -} - -func (eduvpn *VPNState) getCurrentState() (*FSMState, error) { - state, hasState := eduvpn.FSM.States[eduvpn.FSM.Current] - - if !hasState { - return nil, errors.New("Cannot get current state") - } - - return state, nil -} - -func FindFSMState(state FSMStateID, fsm *FSM) *FSM { - if fsm == nil { - return nil - } - - // Check if the state is in the current fsm - retrievedState, hasState := fsm.States[state] - - // Otherwise we need to go to the sub states - if !hasState || retrievedState == nil { - return FindFSMState(state, fsm.States[fsm.Current].Sub) - } else { - return fsm - } -} - -func (eduvpn *VPNState) IsInFSMState(check FSMStateID) bool { - return eduvpn.FSM.Current == check -} - -func (eduvpn *VPNState) findTransition(check FSMStateID) (*FSM, bool) { - fsm := FindFSMState(check, eduvpn.FSM) - - if fsm == nil { - return nil, false - } - - subStates := fsm.States[fsm.Current].Sub - - if subStates != nil { - if subStates.States[subStates.Current].Locked { - return nil, false - } - } - for _, val := range fsm.States[fsm.Current].Transition { - if val == check { - return fsm, true - } - } - - return nil, false -} - -func (eduvpn *VPNState) HasTransition(check FSMStateID) bool { - fsm, ok := eduvpn.findTransition(check) - - return ok && fsm != nil + return false } func (eduvpn *VPNState) InState(check FSMStateID) bool { - fsm := FindFSMState(check, eduvpn.FSM) - - if fsm == nil { - return false - } - - return fsm.Current == check + return check == eduvpn.FSM.Current } func (eduvpn *VPNState) writeGraph() { @@ -178,11 +84,11 @@ func (eduvpn *VPNState) writeGraph() { } func (eduvpn *VPNState) GoTransition(newState FSMStateID, data string) bool { - fsm, ok := eduvpn.findTransition(newState) + ok := eduvpn.HasTransition(newState) if ok { - oldState := fsm.Current - fsm.Current = newState + oldState := eduvpn.FSM.Current + eduvpn.FSM.Current = newState if eduvpn.Debug { eduvpn.writeGraph() } @@ -192,94 +98,31 @@ func (eduvpn *VPNState) GoTransition(newState FSMStateID, data string) bool { return ok } -func getGraphviz(fsm *FSM, graph string, current bool) string { - if fsm == nil { - return graph - } - - for name, state := range fsm.States { - for _, transition := range state.Transition { - graph += "\n" + "cluster_" + name.String() + " -> cluster_" + transition.String() - } - - graph += "\nsubgraph cluster_" + name.String() + "{\n" - if state.Locked { - graph += "style=\"dotted\"\n" - } else { - graph += "style=\"\"\n" - } - if fsm.Current == name { - color := "orange" - if current { - color = "blue" - } - graph += fmt.Sprintf("color=\"%s\"\n", color) - graph += fmt.Sprintf("fontcolor=\"%s\"\n", color) - } else { - graph += "color=\"\"\n" - graph += "fontcolor=\"\"\n" +func (eduvpn *VPNState) GenerateGraph() string { + graph := `digraph eduvpn_fsm { +nodesep = 2; +rankdir = LR;` + graph += "\nnode[color=blue]; " + eduvpn.FSM.Current.String() + ";\n" + graph += "node [color=black];\n" + for state, transitions := range eduvpn.FSM.States { + for _, transition_state := range transitions { + graph += state.String() + " -> " + transition_state.String() + "\n" } - graph += "label=" + name.String() - graph = getGraphviz(state.Sub, graph, current && fsm.Current == name) - graph += "\n}" } - return graph -} - -func (eduvpn *VPNState) GenerateGraph() string { - graph := `digraph fsm_with_legend { -nodesep=2 -subgraph fsm { -nodesep=2` - graph = getGraphviz(eduvpn.FSM, graph, true) - graph += "\n}" - graph += `graph [labelloc="b" labeljust="r" label=< -<TABLE BORDER="2" CELLBORDER="1" CELLSPACING="0"> -<TR><TD BGCOLOR="BLUE"><font color="white">The current state</font></TD></TR> -<TR><TD BGCOLOR="ORANGE">A state that is not the current state but will be once the parent state becomes the current</TD></TR> -</TABLE>>]; -}` - + graph += "}" return graph } func (eduvpn *VPNState) InitializeFSM() { - // The states when a server is authenticated - serverAuthenticated := &FSMState{Sub: &FSM{States: FSMStates{ - SERVER_DISCONNECTED: {Transition: FSMTransitions{SERVER_CONNECTED}}, - SERVER_CONNECTED: {Transition: FSMTransitions{SERVER_DISCONNECTED}}, - }, Current: SERVER_DISCONNECTED}, Transition: FSMTransitions{SERVER_NOT_AUTHENTICATED}} - - // The states when a server is not authenticated - serverNotAuthenticated := &FSMState{Sub: &FSM{States: FSMStates{ - // In this state we cannot exit to the parent state - // As the parent state can go to authenticated - SERVER_INITIALIZED: {Transition: FSMTransitions{SERVER_OAUTH_STARTED}, Locked: true}, - - // The state that indicates oauth is in progress - SERVER_OAUTH_STARTED: {Transition: FSMTransitions{SERVER_OAUTH_FINISHED}, Locked: true}, - SERVER_OAUTH_FINISHED: {Transition: FSMTransitions{SERVER_OAUTH_STARTED}}, - }, Current: SERVER_INITIALIZED}, Transition: FSMTransitions{SERVER_AUTHENTICATED}} - - // The states of the server, it has authenticated and not authenticated ass sub states - serverStates := &FSMState{Sub: &FSM{States: FSMStates{ - SERVER_AUTHENTICATED: serverAuthenticated, - SERVER_NOT_AUTHENTICATED: serverNotAuthenticated, - }, Current: SERVER_NOT_AUTHENTICATED}, Transition: FSMTransitions{CONFIG_NOSERVER}} - - // The state when a server is registered - registeredState := &FSMState{Sub: &FSM{States: FSMStates{ - // When no server has been chosen, we have no sub states - CONFIG_NOSERVER: {Transition: FSMTransitions{CONFIG_CHOSENSERVER}}, - // A server has been chosen, it has substates such as oauth, connected and disconnected - CONFIG_CHOSENSERVER: serverStates, - }, Current: CONFIG_NOSERVER}, Transition: FSMTransitions{APP_DEREGISTERED}} - - deregisteredState := &FSMState{Transition: FSMTransitions{APP_REGISTERED}} - eduvpn.FSM = &FSM{ - States: FSMStates{ - APP_REGISTERED: registeredState, APP_DEREGISTERED: deregisteredState, - }, Current: APP_DEREGISTERED, + States: FSMStates { + DEREGISTERED: {NO_SERVER}, + NO_SERVER: {CHOSEN_SERVER}, + CHOSEN_SERVER: {AUTHENTICATED, OAUTH_STARTED}, + OAUTH_STARTED: {AUTHENTICATED}, + AUTHENTICATED: {CONNECTED}, + CONNECTED: {AUTHENTICATED}, + }, + Current: DEREGISTERED, } } |
