-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathsenml.go
More file actions
148 lines (135 loc) · 3.11 KB
/
senml.go
File metadata and controls
148 lines (135 loc) · 3.11 KB
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
package senml
import (
"encoding/xml"
"sort"
"time"
)
// Pack defines a SenML pack (a list of Records).
type Pack []Record
// Equals checks if 2 packs are equal.
func (p Pack) Equals(p2 Pack) bool {
if (p == nil && p2 != nil) || (p != nil && p2 == nil) {
return false
}
if len(p) != len(p2) {
return false
}
for i := range p {
if !p[i].Equals(&p2[i]) {
return false
}
}
return true
}
// Normalize resolves the SenML Records, as explained in https://tools.ietf.org/html/draft-ietf-core-senml-16#section-4.6.
// All base items are removed, and records are sorted in chronological order.
func (p Pack) Normalize() Pack {
var bname string
var bunit Unit
var btime, bval, bsum float64
var bver int
n := make(Pack, 0, len(p))
for i := range p {
if p[i].BaseTime != 0 {
btime = p[i].BaseTime
}
if p[i].BaseVersion != 0 {
bver = p[i].BaseVersion
}
if p[i].BaseUnit != "" {
bunit = p[i].BaseUnit
}
if p[i].BaseName != "" {
bname = p[i].BaseName
}
if p[i].BaseValue != nil {
bval = *p[i].BaseValue
}
if p[i].BaseSum != nil {
bsum = *p[i].BaseSum
}
r := Record{
Name: bname + p[i].Name,
Time: btime + p[i].Time,
Unit: bunit,
BaseVersion: bver,
}
if p[i].Unit != "" {
r.Unit = p[i].Unit
}
switch {
case p[i].Value != nil:
nval := bval + *p[i].Value
r.Value = &nval
case p[i].BoolValue != nil:
r.BoolValue = p[i].BoolValue
case p[i].StringValue != "":
r.StringValue = p[i].StringValue
case len(p[i].DataValue) > 0:
r.DataValue = p[i].DataValue
case p[i].Sum != nil:
nsum := bsum + *p[i].Sum
r.Sum = &nsum
default:
continue
}
n = append(n, r)
}
sort.Sort(&n)
return n
}
// NormalizeAt resolves the SenML Records, and replaces all relative times
// by absolute times, based on the t reference time.
func (p Pack) NormalizeAt(t time.Time) Pack {
n := p.Normalize()
rt := Time(t)
for i := range n {
n[i].Time = absoluteTime(n[i].Time, rt)
}
return n
}
// Len implements sort.Interface.
func (p Pack) Len() int {
return len(p)
}
// Less implements sort.Interface.
func (p Pack) Less(i, j int) bool {
return p[i].Time < p[j].Time
}
// Swap implements sort.Interface.
func (p Pack) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
// MarshalXML implements xml.Marshaler. It encodes the SenML Pack to XML.
func (p Pack) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
n := xmlPack{
Xmlns: "urn:ietf:params:xml:ns:senml",
Records: make([]xmlRecord, len(p)),
}
for i := range p {
n.Records[i].Record = p[i]
}
return e.Encode(n)
}
// UnmarshalXML implements xml.Unmarshaler. It decodes a XML encoded SenML Pack.
func (p *Pack) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
n := xmlPack{}
err := d.DecodeElement(&n, &start)
if err != nil {
return err
}
*p = make(Pack, len(n.Records))
for i := range *p {
(*p)[i] = n.Records[i].Record
}
return nil
}
type xmlPack struct {
XMLName *bool `xml:"sensml"`
Xmlns string `xml:"xmlns,attr"`
Records []xmlRecord `xml:"senml"`
}
type xmlRecord struct {
XMLName *bool `json:"_,omitempty" xml:"senml"`
Record `xml:",innerxml"`
}