Skip to content

Commit b09bcac

Browse files
authored
add pack.NormalizeAt, Time and GoTime (#11)
* add pack.NormalizeAt, Time and GoTime
1 parent 8f58a71 commit b09bcac

File tree

5 files changed

+198
-76
lines changed

5 files changed

+198
-76
lines changed

record.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package senml
2+
3+
import "time"
4+
5+
// Record is a SenML Record.
6+
type Record struct {
7+
BaseName string `json:"bn,omitempty" xml:"bn,attr,omitempty"`
8+
BaseTime float64 `json:"bt,omitempty" xml:"bt,attr,omitempty"`
9+
BaseUnit Unit `json:"bu,omitempty" xml:"bu,attr,omitempty"`
10+
BaseValue *float64 `json:"bv,omitempty" xml:"bv,attr,omitempty"`
11+
BaseSum *float64 `json:"bs,omitempty" xml:"bs,attr,omitempty"`
12+
13+
Version int `json:"bver,omitempty" xml:"bver,attr,omitempty"`
14+
15+
Name string `json:"n,omitempty" xml:"n,attr,omitempty"`
16+
Unit Unit `json:"u,omitempty" xml:"u,attr,omitempty"`
17+
18+
Time float64 `json:"t,omitempty" xml:"t,attr,omitempty"`
19+
UpdateTime float64 `json:"ut,omitempty" xml:"ut,attr,omitempty"`
20+
21+
Value *float64 `json:"v,omitempty" xml:"v,attr,omitempty"`
22+
StringValue string `json:"vs,omitempty" xml:"vs,attr,omitempty"`
23+
DataValue []byte `json:"vd,omitempty" xml:"vd,attr,omitempty"`
24+
BoolValue *bool `json:"vb,omitempty" xml:"vb,attr,omitempty"`
25+
Sum *float64 `json:"s,omitempty" xml:"s,attr,omitempty"`
26+
}
27+
28+
// Equals checks if two records are equal
29+
func (r *Record) Equals(r2 *Record) bool {
30+
if (r == nil && r2 != nil) || (r != nil && r2 == nil) {
31+
return false
32+
}
33+
if r.BaseName != r2.BaseName {
34+
return false
35+
}
36+
if r.BaseTime != r2.BaseTime {
37+
return false
38+
}
39+
if r.BaseUnit != r2.BaseUnit {
40+
return false
41+
}
42+
if r.Version != r2.Version {
43+
return false
44+
}
45+
if r.Name != r2.Name {
46+
return false
47+
}
48+
if r.Unit != r2.Unit {
49+
return false
50+
}
51+
if r.Time != r2.Time {
52+
return false
53+
}
54+
if r.UpdateTime != r2.UpdateTime {
55+
return false
56+
}
57+
58+
if r.Value != nil && r2.Value != nil && *r.Value == *r2.Value {
59+
return true
60+
}
61+
if r.StringValue != "" && r.StringValue == r2.StringValue {
62+
return true
63+
}
64+
if r.BoolValue != nil && r2.BoolValue != nil && *r.BoolValue == *r2.BoolValue {
65+
return true
66+
}
67+
if r.Sum != nil && r2.Sum != nil && *r.Sum == *r2.Sum {
68+
return true
69+
}
70+
if r.DataValue != nil && r2.DataValue != nil && len(r.DataValue) == len(r2.DataValue) {
71+
for i := range r.DataValue {
72+
if r.DataValue[i] != r2.DataValue[i] {
73+
return false
74+
}
75+
}
76+
return true
77+
}
78+
return false
79+
}
80+
81+
// GoTime returns the Time of the Record as a Go time.Time.
82+
func (r *Record) GoTime() time.Time {
83+
return GoTime(r.Time)
84+
}

senml.go

Lines changed: 12 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package senml
33
import (
44
"encoding/xml"
55
"sort"
6+
"time"
67
)
78

89
// Pack defines a SenML pack (a list of Records).
@@ -24,82 +25,6 @@ func (p Pack) Equals(p2 Pack) bool {
2425
return true
2526
}
2627

27-
// Record is a SenML Record.
28-
type Record struct {
29-
BaseName string `json:"bn,omitempty" xml:"bn,attr,omitempty"`
30-
BaseTime float64 `json:"bt,omitempty" xml:"bt,attr,omitempty"`
31-
BaseUnit Unit `json:"bu,omitempty" xml:"bu,attr,omitempty"`
32-
BaseValue *float64 `json:"bv,omitempty" xml:"bv,attr,omitempty"`
33-
BaseSum *float64 `json:"bs,omitempty" xml:"bs,attr,omitempty"`
34-
35-
Version int `json:"bver,omitempty" xml:"bver,attr,omitempty"`
36-
37-
Name string `json:"n,omitempty" xml:"n,attr,omitempty"`
38-
Unit Unit `json:"u,omitempty" xml:"u,attr,omitempty"`
39-
40-
Time float64 `json:"t,omitempty" xml:"t,attr,omitempty"`
41-
UpdateTime float64 `json:"ut,omitempty" xml:"ut,attr,omitempty"`
42-
43-
Value *float64 `json:"v,omitempty" xml:"v,attr,omitempty"`
44-
StringValue string `json:"vs,omitempty" xml:"vs,attr,omitempty"`
45-
DataValue []byte `json:"vd,omitempty" xml:"vd,attr,omitempty"`
46-
BoolValue *bool `json:"vb,omitempty" xml:"vb,attr,omitempty"`
47-
Sum *float64 `json:"s,omitempty" xml:"s,attr,omitempty"`
48-
}
49-
50-
// Equals checks if two records are equal
51-
func (r *Record) Equals(r2 *Record) bool {
52-
if (r == nil && r2 != nil) || (r != nil && r2 == nil) {
53-
return false
54-
}
55-
if r.BaseName != r2.BaseName {
56-
return false
57-
}
58-
if r.BaseTime != r2.BaseTime {
59-
return false
60-
}
61-
if r.BaseUnit != r2.BaseUnit {
62-
return false
63-
}
64-
if r.Version != r2.Version {
65-
return false
66-
}
67-
if r.Name != r2.Name {
68-
return false
69-
}
70-
if r.Unit != r2.Unit {
71-
return false
72-
}
73-
if r.Time != r2.Time {
74-
return false
75-
}
76-
if r.UpdateTime != r2.UpdateTime {
77-
return false
78-
}
79-
80-
if r.Value != nil && r2.Value != nil && *r.Value == *r2.Value {
81-
return true
82-
}
83-
if r.StringValue != "" && r.StringValue == r2.StringValue {
84-
return true
85-
}
86-
if r.BoolValue != nil && r2.BoolValue != nil && *r.BoolValue == *r2.BoolValue {
87-
return true
88-
}
89-
if r.Sum != nil && r2.Sum != nil && *r.Sum == *r2.Sum {
90-
return true
91-
}
92-
if r.DataValue != nil && r2.DataValue != nil && len(r.DataValue) == len(r2.DataValue) {
93-
for i := range r.DataValue {
94-
if r.DataValue[i] != r2.DataValue[i] {
95-
return false
96-
}
97-
}
98-
return true
99-
}
100-
return false
101-
}
102-
10328
// Normalize resolves the SenML Records, as explained in https://tools.ietf.org/html/draft-ietf-core-senml-16#section-4.6.
10429
// All base items are removed, and records are sorted in chronological order.
10530
func (p Pack) Normalize() Pack {
@@ -159,6 +84,17 @@ func (p Pack) Normalize() Pack {
15984
return n
16085
}
16186

87+
// NormalizeAt resolves the SenML Records, and replaces all relative times
88+
// by absolute times, based on the t reference time.
89+
func (p Pack) NormalizeAt(t time.Time) Pack {
90+
n := p.Normalize()
91+
rt := Time(t)
92+
for i := range n {
93+
n[i].Time = absoluteTime(n[i].Time, rt)
94+
}
95+
return n
96+
}
97+
16298
// Len implements sort.Interface.
16399
func (p Pack) Len() int {
164100
return len(p)

senml_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"encoding/xml"
66
"testing"
7+
"time"
78
)
89

910
func TestEquals(t *testing.T) {
@@ -338,6 +339,66 @@ func TestNormalize(t *testing.T) {
338339
}
339340
}
340341

342+
func TestNormalizeAt(t *testing.T) {
343+
t0 := time.Now()
344+
tcs := []struct {
345+
src Pack
346+
norm Pack
347+
}{
348+
{
349+
src: Pack{
350+
{BaseName: "foo."},
351+
{Name: "bar", Value: Float(1)},
352+
},
353+
norm: Pack{
354+
{Name: "foo.bar", Time: Time(t0), Value: Float(1)},
355+
},
356+
},
357+
{
358+
src: Pack{
359+
{BaseName: "foo."},
360+
{Name: "bar", Time: 1, Value: Float(1)},
361+
},
362+
norm: Pack{
363+
{Name: "foo.bar", Time: Time(t0) + 1, Value: Float(1)},
364+
},
365+
},
366+
{
367+
src: Pack{
368+
{BaseName: "foo."},
369+
{Name: "bar", Time: -268435457, Value: Float(1)},
370+
},
371+
norm: Pack{
372+
{Name: "foo.bar", Time: Time(t0) - 268435457, Value: Float(1)},
373+
},
374+
},
375+
{
376+
src: Pack{
377+
{BaseName: "foo."},
378+
{Name: "bar", Time: 268435456, Value: Float(1)},
379+
},
380+
norm: Pack{
381+
{Name: "foo.bar", Time: Time(t0) + 268435456, Value: Float(1)},
382+
},
383+
},
384+
{
385+
src: Pack{
386+
{BaseName: "foo."},
387+
{Name: "bar", Time: 268435457, Value: Float(1)},
388+
},
389+
norm: Pack{
390+
{Name: "foo.bar", Time: 268435457, Value: Float(1)},
391+
},
392+
},
393+
}
394+
for _, tc := range tcs {
395+
norm := tc.src.NormalizeAt(t0)
396+
if !norm.Equals(tc.norm) {
397+
t.Errorf("Normalized version of %+v should be %+v not %+v", tc.src, tc.norm, norm)
398+
}
399+
}
400+
}
401+
341402
func TestJSON(t *testing.T) {
342403
tcs := []struct {
343404
src Pack

time.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package senml
2+
3+
import (
4+
"math"
5+
"time"
6+
)
7+
8+
// Time converts a Go time.Time to a SenML time (seconds since the epoch as a floating point number).
9+
func Time(t time.Time) float64 {
10+
return float64(t.UnixNano()) / float64(time.Second)
11+
}
12+
13+
// GoTime converts a SenML time to a Go time.Time.
14+
func GoTime(f float64) time.Time {
15+
sec := math.Trunc(f)
16+
nsec := f*float64(time.Second) - sec*float64(time.Second)
17+
return time.Unix(int64(sec), int64(nsec))
18+
}
19+
20+
var maxRelativeTime = math.Pow(2, 28)
21+
22+
func absoluteTime(t float64, ref float64) float64 {
23+
if t <= maxRelativeTime {
24+
return t + ref
25+
}
26+
return t
27+
}

time_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package senml
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
func TestTime(t *testing.T) {
9+
t0 := time.Date(2018, 07, 11, 0, 0, 0, 100000000, time.UTC)
10+
st := Time(t0)
11+
if !GoTime(st).Equal(t0) {
12+
t.Errorf("Time(GoTime) does not return the right time")
13+
}
14+
}

0 commit comments

Comments
 (0)