-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathavsm.go
128 lines (112 loc) · 3.49 KB
/
avsm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package avsm
import (
"fmt"
"sync"
)
// State denotes different states of the vehicle.
type State string
const (
Ready = "Ready"
Riding = "Riding"
BatteryLow = "BatteryLow"
Bounty = "Bounty"
Collected = "Collected"
Dropped = "Dropped"
ServiceMode = "ServiceMode"
Terminated = "Terminated"
Unknown = "Unknown"
)
// Role denotes user roles who operates the vehicle
type Role string
const (
Automatic = "Automatic"
Admin = "Admin"
EndUser= "EndUser"
Hunter= "Hunter"
)
// Abstract Vehicle State Machine
type Vehicle struct {
state State
mu sync.RWMutex
transitions map[State]map[State][]Role
}
// Initial state and Transition rules are created.Then set to vehicle struct.
func (v *Vehicle)SetStateTransitionRules(){
v.mu.RLock()
defer v.mu.RUnlock()
// Entire valid transitions are created as State Map contains State Map of List of Roles
transitions := map[State]map[State][]Role{
Ready: map[State][]Role{
Bounty:{Automatic},Riding:{Admin,EndUser,Hunter},Unknown:{Automatic}},
BatteryLow:map[State][]Role{
Bounty:{Automatic},},
Bounty:map[State][]Role{
Collected:{Hunter},},
Riding:map[State][]Role{
Ready:{Admin,EndUser,Hunter},BatteryLow:{Automatic},},
Collected:map[State][]Role{
Dropped:{Hunter},},
Dropped:map[State][]Role{
Ready:{Hunter},},
Unknown:{},
Terminated:{},
ServiceMode:{},
}
//Initial state is set to Ready and hardcoded
v.state = Ready
// Valid transitions are set to Vehicles transition
v.transitions = transitions
}
// CurrentState returns the machine's current state. If the State returned is
// "", then the machine has not been given an initial state.
func (v *Vehicle) CurrentState() State {
v.mu.RLock()
defer v.mu.RUnlock()
return v.state
}
//Main state transition logic
func (v *Vehicle) StateTransition(toState State, role Role) error {
v.mu.Lock()
defer v.mu.Unlock()
// if this is nil we cannot assume any state.Though currently initial state is hardcoded
// Below check is not called,will be used for future purpose
if v.transitions == nil {
return newErrorStruct("no states added to the Vehicle", ErrorVehicleNotInitialized)
}
// if the state is nothing, this is probably the initial state
// Below check is not called,will be used for future purpose
if v.state == "" {
// if the state is not defined, it's invalid
if _, ok := v.transitions[toState]; !ok {
return newErrorStruct("the initial state has not been defined within the machine", ErrorStateUndefined)
}
// set the state
v.state = toState
return nil
}
// if the destination state was not defined...
// Below check is not called,will be used for future purpose
if _, ok := v.transitions[toState]; !ok {
return newErrorStruct(fmt.Sprintf("state %s has not been registered", toState), ErrorStateUndefined)
}
// if we are not permitted to transition to this state...
// Role Admin can perform any transition state
if role != Admin {
roles, ok := v.transitions[v.state][toState];
if !ok {
return newErrorStruct(fmt.Sprintf("transition from state %s to %s is not permitted", v.state, toState), ErrorTransitionNotPermitted)
}
for _, ok := range roles {
if role == ok {
// set the state
v.state = toState
// Return nil when transitions are valid
return nil
}
}
return newErrorStruct(fmt.Sprintf("Invalid permission transition from state %s to %s for a role %s", v.state, toState, role), ErrorRolePermissionDenied)
}
// set the state
v.state = toState
return nil
}