-
Notifications
You must be signed in to change notification settings - Fork 6
/
window.go
151 lines (132 loc) · 4.37 KB
/
window.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package ais
import (
"bytes"
"fmt"
"time"
)
// Window is used to create a convolution algorithm that slides down a RecordSet
// and performs analysis on Records that are within the a time window.
type Window struct {
leftMarker, rightMarker time.Time
timeIndex int
width time.Duration
Data map[uint64]*Record
}
// NewWindow returns a *Window with the left marker set to the time in
// the next record read from the RecordSet. The Window width will be set from
// the argument provided and the righ marker will be derived from left and width.
// When creating a Window right after opening a RecordSet then the Window
// will be set to first Record in the set, but that first record will still be
// available to the client's first call to rs.Read(). For any non-nil error
// NewWindow returns nil and the error.
func NewWindow(rs *RecordSet, width time.Duration) (*Window, error) {
win := new(Window)
timeIndex, ok := rs.Headers().Contains("BaseDateTime")
if !ok {
return nil, fmt.Errorf("newwindow: headers does not contain BaseDateTime")
}
win.SetIndex(timeIndex)
rec, err := rs.readFirst()
if err != nil {
return nil, fmt.Errorf("newwindow: %v", err)
}
t, err := time.Parse(TimeLayout, (*rec)[timeIndex])
if err != nil {
return nil, fmt.Errorf("newwindow: %v", err)
}
win.SetLeft(t)
win.SetWidth(width)
win.SetRight(win.Left().Add(win.Width()))
return win, nil
}
// Left returns the left marker.
func (win *Window) Left() time.Time { return win.leftMarker }
// SetLeft defines the left marker for the Window
func (win *Window) SetLeft(marker time.Time) {
win.leftMarker = marker
}
// Right returns the right marker.
func (win *Window) Right() time.Time { return win.rightMarker }
// SetRight defines the right marker of the Window.
func (win *Window) SetRight(marker time.Time) {
win.rightMarker = marker
}
// Width returns the width of the Window.
func (win *Window) Width() time.Duration { return win.width }
// SetWidth provides the block of time coverd by the Window.
func (win *Window) SetWidth(dur time.Duration) {
win.width = dur
}
// SetIndex provides the integer index of the BaseDateTime field the
// Records stored in the Window.
func (win *Window) SetIndex(index int) {
win.timeIndex = index
}
// AddRecord appends a new Record to the data in the Window.
func (win *Window) AddRecord(rec Record) {
if win.Data == nil {
win.Data = make(map[uint64]*Record)
}
h := rec.Hash()
// fmt.Println("hash is: ", h)
win.Data[h] = &rec
}
// InWindow tests if a time is in the Window.
func (win *Window) InWindow(t time.Time) bool {
if win.leftMarker.Equal(t) {
return true
}
return win.leftMarker.Before(t) && t.Before(win.rightMarker)
}
// RecordInWindow returns true if the record is in the Window.
// Errors are possible from parsing the BaseDateTime field of the
// Record.
func (win *Window) RecordInWindow(rec *Record) (bool, error) {
t, err := rec.ParseTime(win.timeIndex)
if err != nil {
return false, fmt.Errorf("recordinwindow: %v", err)
}
return win.InWindow(t), nil
}
// Slide moves the window down by the time provided in the arugment dur.
// Slide also removes any data from the Window that would no longer return
// true from InWindow for the new left and right markers after the Slide.
func (win *Window) Slide(dur time.Duration) {
win.SetLeft(win.leftMarker.Add(dur))
win.SetRight(win.leftMarker.Add(win.Width()))
win.validate()
}
// Len returns the lenght of the slice holding the Records in the Window
func (win *Window) Len() int {
return len(win.Data)
}
// Validate checks the data held by the Window to ensure every Record passes
// the InWindow test.
func (win *Window) validate() error {
for hash, rec := range win.Data {
in, err := win.RecordInWindow(rec)
if err != nil {
return err
}
if !in {
delete(win.Data, hash)
}
}
return nil
}
// String implements the Stringer interface for Window.
func (win *Window) String() string {
var buf bytes.Buffer
for _, rec := range win.Data {
fmt.Fprintln(&buf, rec)
}
return buf.String()
}
// Config provides a pretty print of the Window's configuration
func (win *Window) Config() string {
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("Window has the following configuration\n"))
buf.WriteString(fmt.Sprintf("\tLeft marker: %s\n", win.Left().Format(TimeLayout)))
buf.WriteString(fmt.Sprintf("\tWindow width: %v\n", win.Width()))
return buf.String()
}