forked from influxdata/kapacitor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
derivative.go
116 lines (105 loc) · 2.62 KB
/
derivative.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
package kapacitor
import (
"log"
"time"
"github.com/influxdata/kapacitor/models"
"github.com/influxdata/kapacitor/pipeline"
)
type DerivativeNode struct {
node
d *pipeline.DerivativeNode
}
// Create a new derivative node.
func newDerivativeNode(et *ExecutingTask, n *pipeline.DerivativeNode, l *log.Logger) (*DerivativeNode, error) {
dn := &DerivativeNode{
node: node{Node: n, et: et, logger: l},
d: n,
}
// Create stateful expressions
dn.node.runF = dn.runDerivative
return dn, nil
}
func (d *DerivativeNode) runDerivative([]byte) error {
switch d.Provides() {
case pipeline.StreamEdge:
previous := make(map[models.GroupID]models.Point)
for p, ok := d.ins[0].NextPoint(); ok; p, ok = d.ins[0].NextPoint() {
pr, ok := previous[p.Group]
if !ok {
previous[p.Group] = p
continue
}
value, ok := d.derivative(pr.Fields, p.Fields, pr.Time, p.Time)
if ok {
fields := pr.Fields.Copy()
fields[d.d.As] = value
pr.Fields = fields
for _, child := range d.outs {
err := child.CollectPoint(pr)
if err != nil {
return err
}
}
}
previous[p.Group] = p
}
case pipeline.BatchEdge:
for b, ok := d.ins[0].NextBatch(); ok; b, ok = d.ins[0].NextBatch() {
if len(b.Points) > 0 {
pr := b.Points[0]
var p models.BatchPoint
for i := 1; i < len(b.Points); i++ {
p = b.Points[i]
value, ok := d.derivative(pr.Fields, p.Fields, pr.Time, p.Time)
if ok {
fields := pr.Fields.Copy()
fields[d.d.As] = value
b.Points[i-1].Fields = fields
} else {
b.Points = append(b.Points[:i-1], b.Points[i:]...)
i--
}
pr = p
}
b.Points = b.Points[:len(b.Points)-1]
}
for _, child := range d.outs {
err := child.CollectBatch(b)
if err != nil {
return err
}
}
}
}
return nil
}
func (d *DerivativeNode) derivative(prev, curr models.Fields, prevTime, currTime time.Time) (float64, bool) {
f0, ok := numToFloat(prev[d.d.Field])
if !ok {
d.logger.Printf("E! cannot apply derivative to type %T", prev[d.d.Field])
return 0, false
}
f1, ok := numToFloat(curr[d.d.Field])
if !ok {
d.logger.Printf("E! cannot apply derivative to type %T", curr[d.d.Field])
return 0, false
}
elapsed := float64(currTime.Sub(prevTime))
diff := f1 - f0
// Drop negative values for non-negative derivatives
if d.d.NonNegativeFlag && diff < 0 {
return 0, false
}
value := float64(diff) / (elapsed / float64(d.d.Unit))
return value, true
}
func numToFloat(num interface{}) (float64, bool) {
switch n := num.(type) {
case int64:
return float64(n), true
case float64:
return n, true
default:
return 0, false
}
}