forked from digineo/go-ping
/
history.go
123 lines (104 loc) · 2.43 KB
/
history.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
package monitor
import (
"math"
"sort"
"sync"
"time"
)
// Result stores the information about a single ping, in particular
// the round-trip time or whether the packet was lost.
type Result struct {
RTT time.Duration
Lost bool
}
// History represents the ping history for a single node/device.
type History struct {
capacity int
results []Result
sync.RWMutex
}
// NewHistory creates a new History object with a specific capacity
func NewHistory(capacity int) History {
return History{
capacity: capacity,
results: make([]Result, 0, capacity+1),
}
}
// AddResult saves a ping result into the internal history.
func (h *History) AddResult(rtt time.Duration, err error) {
h.Lock()
h.results = append(h.results, Result{RTT: rtt, Lost: err != nil})
if len(h.results) > h.capacity {
h.results = h.results[1:]
}
h.Unlock()
}
func (h *History) clear() {
}
// ComputeAndClear aggregates the result history into a single data point and clears the result set.
func (h *History) ComputeAndClear() *Metrics {
h.Lock()
result := h.compute()
h.clear()
h.Unlock()
return result
}
// Compute aggregates the result history into a single data point.
func (h *History) Compute() *Metrics {
h.RLock()
defer h.RUnlock()
return h.compute()
}
func (h *History) compute() *Metrics {
numFailure := 0
numTotal := len(h.results)
µsPerMs := 1.0 / float64(time.Millisecond)
if numTotal == 0 {
return nil
}
data := make([]float64, 0, numTotal)
var best, worst, mean, stddev, total, sumSquares float64
var extremeFound bool
for i := 0; i < numTotal; i++ {
curr := &h.results[i]
if curr.Lost {
numFailure++
} else {
rtt := float64(curr.RTT) * µsPerMs
data = append(data, rtt)
if !extremeFound || rtt < best {
best = rtt
}
if !extremeFound || rtt > worst {
worst = rtt
}
extremeFound = true
total += rtt
}
}
size := float64(numTotal - numFailure)
mean = total / size
for _, rtt := range data {
sumSquares += math.Pow(rtt-mean, 2)
}
stddev = math.Sqrt(sumSquares / size)
median := math.NaN()
if l := len(data); l > 0 {
sort.Float64Slice(data).Sort()
if l%2 == 0 {
median = (data[l/2-1] + data[l/2]) / 2
} else {
median = data[l/2]
}
}
return &Metrics{
Results: h.results,
PacketsSent: numTotal,
PacketsLost: numFailure,
Best: float32(best),
Worst: float32(worst),
Median: float32(median),
Mean: float32(mean),
StdDev: float32(stddev),
}
}