forked from orkestr8/fsm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflap.go
120 lines (102 loc) · 2.47 KB
/
flap.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
package fsm // import "github.com/orkestr8/fsm"
// Flap is oscillation between two adjacent states. For example, a->b followed by b->a is
// counted as 1 flap. Similarly, b->a followed by a->b is another flap.
type Flap struct {
States [2]Index
Count int
Raise Signal
}
func (s *spec) flap(a, b Index) *Flap {
key := [2]Index{a, b}
if a > b {
key = [2]Index{b, a}
}
if f, has := s.flaps[key]; has {
return f
}
return nil
}
// compileFlappingMust is a Must version (will panic if err) of CheckFlapping
func (s *spec) compileFlappingMust(checks []Flap) *spec {
_, err := s.compileFlapping(checks)
if err != nil {
panic(err)
}
return s
}
// compileFlapping - Limit is the maximum of a->b b->a transitions allowable. For detecting
// oscillations between two adjacent states (no hops). This method simply checks in the
// input configuraton and updates the spec.
func (s *spec) compileFlapping(checks []Flap) (*spec, error) {
flaps := map[[2]Index]*Flap{}
for _, check := range checks {
// check the state
for _, state := range check.States {
if _, has := s.states[state]; !has {
return nil, ErrUnknownState{spec: s, Index: state}
}
}
key := [2]Index{check.States[0], check.States[1]}
if check.States[0] > check.States[1] {
key = [2]Index{check.States[1], check.States[0]}
}
copy := check
flaps[key] = ©
}
s.flaps = flaps
return s, nil
}
func newFlaps() *flaps {
return &flaps{
history: []Index{},
}
}
type flaps struct {
history []Index
}
func (f *flaps) reset() {
f.history = []Index{}
}
func equals(i, j []Index) bool {
if len(j) != len(i) {
return false
}
for k := range j {
if i[k] != j[k] {
return false
}
}
return true
}
func (f *flaps) record(a, b Index) {
// old := append([]Index{}, f.history...)
// defer func() { log.Debug("record", "before", old, "a", a, "b", b, "after", f.history) }()
if len(f.history) == 0 {
f.history = []Index{a, b}
return
}
last := f.history[len(f.history)-2:]
if equals(last, []Index{b, a}) {
f.history = append(f.history, b)
} else {
f.reset()
}
}
func (f *flaps) count(a, b Index) int {
if len(f.history) < 2 {
return 0
}
search := []Index{a, b, a}
if f.history[len(f.history)-1] == b {
search = []Index{b, a, b}
}
count := 0
// defer func() { log.Debug("search", "search", search, "history", f.history, "count", count) }()
for i := len(f.history); i > 2; i = i - 2 {
check := f.history[i-3 : i]
if equals(check, search) {
count++
}
}
return count
}