This repository has been archived by the owner on Sep 3, 2020. It is now read-only.
forked from brocaar/lorawan
-
Notifications
You must be signed in to change notification settings - Fork 0
/
payload.go
287 lines (249 loc) · 6.82 KB
/
payload.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
package lorawan
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
)
// EUI64 data type
type EUI64 [8]byte
// MarshalText implements encoding.TextMarshaler.
func (e EUI64) MarshalText() ([]byte, error) {
return []byte(e.String()), nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (e *EUI64) UnmarshalText(text []byte) error {
b, err := hex.DecodeString(string(text))
if err != nil {
return err
}
if len(e) != len(b) {
return fmt.Errorf("lorawan: exactly %d bytes are expected", len(e))
}
copy(e[:], b)
return nil
}
// String implement fmt.Stringer.
func (e EUI64) String() string {
return hex.EncodeToString(e[:])
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (e EUI64) MarshalBinary() ([]byte, error) {
out := make([]byte, len(e))
// little endian
for i, v := range e {
out[len(e)-i-1] = v
}
return out, nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (e *EUI64) UnmarshalBinary(data []byte) error {
if len(data) != len(e) {
return fmt.Errorf("lorawan: %d bytes of data are expected", len(e))
}
for i, v := range data {
// little endian
e[len(e)-i-1] = v
}
return nil
}
// Scan implements sql.Scanner.
func (e *EUI64) Scan(src interface{}) error {
b, ok := src.([]byte)
if !ok {
return errors.New("lorawan: []byte type expected")
}
if len(b) != len(e) {
return fmt.Errorf("lorawan []byte must have length %d", len(e))
}
copy(e[:], b)
return nil
}
// DevNonce represents a 2 byte dev-nonce.
type DevNonce [2]byte
// String implements fmt.Stringer.
func (n DevNonce) String() string {
return hex.EncodeToString(n[:])
}
// MarshalText implements encoding.TextMarshaler.
func (n DevNonce) MarshalText() ([]byte, error) {
return []byte(n.String()), nil
}
// AppNonce represents a 3 byte app-nonce.
type AppNonce [3]byte
// String implements fmt.Stringer.
func (n AppNonce) String() string {
return hex.EncodeToString(n[:])
}
// MarshalText implements encoding.TextMarshaler.
func (n AppNonce) MarshalText() ([]byte, error) {
return []byte(n.String()), nil
}
// Payload is the interface that every payload needs to implement.
// Since it might be a MACPayload, an indication must be given if
// the direction is uplink or downlink (it has different payloads
// for the same CID, based on direction).
type Payload interface {
MarshalBinary() (data []byte, err error)
UnmarshalBinary(uplink bool, data []byte) error
}
// DataPayload represents a slice of bytes.
type DataPayload struct {
Bytes []byte `json:"bytes"`
}
// MarshalBinary marshals the object in binary form.
func (p DataPayload) MarshalBinary() ([]byte, error) {
return p.Bytes, nil
}
// UnmarshalBinary decodes the object from binary form.
func (p *DataPayload) UnmarshalBinary(uplink bool, data []byte) error {
p.Bytes = make([]byte, len(data))
copy(p.Bytes, data)
return nil
}
// JoinRequestPayload represents the join-request message payload.
type JoinRequestPayload struct {
AppEUI EUI64 `json:"appEUI"`
DevEUI EUI64 `json:"devEUI"`
DevNonce DevNonce `json:"devNonce"`
}
// MarshalBinary marshals the object in binary form.
func (p JoinRequestPayload) MarshalBinary() ([]byte, error) {
out := make([]byte, 0, 18)
b, err := p.AppEUI.MarshalBinary()
if err != nil {
return nil, err
}
out = append(out, b...)
b, err = p.DevEUI.MarshalBinary()
if err != nil {
return nil, err
}
out = append(out, b...)
// little endian
out = append(out, p.DevNonce[1], p.DevNonce[0])
return out, nil
}
// UnmarshalBinary decodes the object from binary form.
func (p *JoinRequestPayload) UnmarshalBinary(uplink bool, data []byte) error {
if len(data) != 18 {
return errors.New("lorawan: 18 bytes of data are expected")
}
if err := p.AppEUI.UnmarshalBinary(data[0:8]); err != nil {
return err
}
if err := p.DevEUI.UnmarshalBinary(data[8:16]); err != nil {
return err
}
// little endian
p.DevNonce[1] = data[16]
p.DevNonce[0] = data[17]
return nil
}
// CFList represents a list of channel frequencies. Each frequency is in Hz
// and must be multiple of 100, (since the frequency will be divided by 100
// on encoding), the max allowed value is 2^24-1 * 100.
type CFList [5]uint32
// MarshalBinary marshals the object in binary form.
func (l CFList) MarshalBinary() ([]byte, error) {
out := make([]byte, 0, 16)
for _, f := range l {
if f%100 != 0 {
return nil, errors.New("lorawan: frequency must be a multiple of 100")
}
f = f / 100
if f > 16777215 { // 2^24 - 1
return nil, errors.New("lorawan: max value of frequency is 2^24-1")
}
b := make([]byte, 4, 4)
binary.LittleEndian.PutUint32(b, f)
out = append(out, b[:3]...)
}
// last byte is 0 / RFU
return append(out, 0), nil
}
// UnmarshalBinary decodes the object from binary form.
func (l *CFList) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return errors.New("lorawan: 16 bytes of data are expected")
}
for i := 0; i < 5; i++ {
l[i] = binary.LittleEndian.Uint32([]byte{
data[i*3],
data[i*3+1],
data[i*3+2],
0,
}) * 100
}
return nil
}
// JoinAcceptPayload represents the join-accept message payload.
type JoinAcceptPayload struct {
AppNonce AppNonce `json:"appNonce"`
NetID NetID `json:"netID"`
DevAddr DevAddr `json:"devAddr"`
DLSettings DLSettings `json:"dlSettings"`
RXDelay uint8 `json:"rxDelay"` // 0=1s, 1=1s, 2=2s, ... 15=15s
CFList *CFList `json:"cFlist"`
}
// MarshalBinary marshals the object in binary form.
func (p JoinAcceptPayload) MarshalBinary() ([]byte, error) {
if p.RXDelay > 15 {
return nil, errors.New("lorawan: the max value of RXDelay is 15")
}
out := make([]byte, 0, 12)
// little endian
for i := len(p.AppNonce) - 1; i >= 0; i-- {
out = append(out, p.AppNonce[i])
}
for i := len(p.NetID) - 1; i >= 0; i-- {
out = append(out, p.NetID[i])
}
b, err := p.DevAddr.MarshalBinary()
if err != nil {
return nil, err
}
out = append(out, b...)
b, err = p.DLSettings.MarshalBinary()
if err != nil {
return nil, err
}
out = append(out, b...)
out = append(out, byte(p.RXDelay))
if p.CFList != nil {
b, err = p.CFList.MarshalBinary()
if err != nil {
return nil, err
}
out = append(out, b...)
}
return out, nil
}
// UnmarshalBinary decodes the object from binary form.
func (p *JoinAcceptPayload) UnmarshalBinary(uplink bool, data []byte) error {
l := len(data)
if l != 12 && l != 28 {
return errors.New("lorawan: 12 or 28 bytes of data are expected (28 bytes if CFList is present)")
}
// little endian
for i, v := range data[0:3] {
p.AppNonce[2-i] = v
}
for i, v := range data[3:6] {
p.NetID[2-i] = v
}
if err := p.DevAddr.UnmarshalBinary(data[6:10]); err != nil {
return err
}
if err := p.DLSettings.UnmarshalBinary(data[10:11]); err != nil {
return err
}
p.RXDelay = uint8(data[11])
if l == 28 {
p.CFList = &CFList{}
if err := p.CFList.UnmarshalBinary(data[12:]); err != nil {
return err
}
}
return nil
}