@@ -16,7 +16,9 @@ import (
16
16
"time"
17
17
18
18
"github.com/gofrs/uuid"
19
+ "github.com/golang/protobuf/ptypes"
19
20
"github.com/gorilla/websocket"
21
+ "github.com/patrickmn/go-cache"
20
22
"github.com/pkg/errors"
21
23
log "github.com/sirupsen/logrus"
22
24
@@ -43,9 +45,10 @@ type Backend struct {
43
45
scheme string
44
46
isClosed bool
45
47
46
- pingInterval time.Duration
47
- readTimeout time.Duration
48
- writeTimeout time.Duration
48
+ statsInterval time.Duration
49
+ pingInterval time.Duration
50
+ readTimeout time.Duration
51
+ writeTimeout time.Duration
49
52
50
53
gateways gateways
51
54
@@ -62,10 +65,11 @@ type Backend struct {
62
65
frequencyMax uint32
63
66
routerConfig structs.RouterConfig
64
67
65
- // diidMap stores the mapping of diid to UUIDs. This should take ~ 1MB of
66
- // memory. Optionaly this could be optimized by letting keys expire after
67
- // a given time.
68
- diidMap map [uint16 ][]byte
68
+ // Cache to store stats.
69
+ statsCache * cache.Cache
70
+
71
+ // Cache to store diid to UUIDs.
72
+ diidCache * cache.Cache
69
73
}
70
74
71
75
// NewBackend creates a new Backend.
@@ -83,15 +87,17 @@ func NewBackend(conf config.Config) (*Backend, error) {
83
87
gatewayStatsChan : make (chan gw.GatewayStats ),
84
88
rawPacketForwarderEventChan : make (chan gw.RawPacketForwarderEvent ),
85
89
86
- pingInterval : conf .Backend .BasicStation .PingInterval ,
87
- readTimeout : conf .Backend .BasicStation .ReadTimeout ,
88
- writeTimeout : conf .Backend .BasicStation .WriteTimeout ,
90
+ statsInterval : conf .Backend .BasicStation .StatsInterval ,
91
+ pingInterval : conf .Backend .BasicStation .PingInterval ,
92
+ readTimeout : conf .Backend .BasicStation .ReadTimeout ,
93
+ writeTimeout : conf .Backend .BasicStation .WriteTimeout ,
89
94
90
95
region : band .Name (conf .Backend .BasicStation .Region ),
91
96
frequencyMin : conf .Backend .BasicStation .FrequencyMin ,
92
97
frequencyMax : conf .Backend .BasicStation .FrequencyMax ,
93
98
94
- diidMap : make (map [uint16 ][]byte ),
99
+ diidCache : cache .New (time .Minute , time .Minute ),
100
+ statsCache : cache .New (conf .Backend .BasicStation .StatsInterval * 2 , conf .Backend .BasicStation .StatsInterval * 2 ),
95
101
}
96
102
97
103
for _ , n := range conf .Filters .NetIDs {
@@ -238,8 +244,10 @@ func (b *Backend) SendDownlinkFrame(df gw.DownlinkFrame) error {
238
244
copy (gatewayID [:], df .GetGatewayId ())
239
245
copy (downID [:], df .GetDownlinkId ())
240
246
247
+ b .incrementTxStats (gatewayID )
248
+
241
249
// store token to UUID mapping
242
- b .diidMap [ uint16 ( df .Token )] = df .GetDownlinkId ()
250
+ b .diidCache . SetDefault ( fmt . Sprintf ( "%d" , df .Token ), df .GetDownlinkId () )
243
251
244
252
websocketSendCounter ("dnmsg" ).Inc ()
245
253
if err := b .sendToGateway (gatewayID , pl ); err != nil {
@@ -382,15 +390,69 @@ func (b *Backend) handleGateway(r *http.Request, c *websocket.Conn) {
382
390
"remote_addr" : r .RemoteAddr ,
383
391
}).Info ("backend/basicstation: gateway connected" )
384
392
393
+ done := make (chan struct {})
394
+
385
395
// remove the gateway on return
386
396
defer func () {
397
+ done <- struct {}{}
387
398
b .gateways .remove (gatewayID )
388
399
log .WithFields (log.Fields {
389
400
"gateway_id" : gatewayID ,
390
401
"remote_addr" : r .RemoteAddr ,
391
402
}).Info ("backend/basicstation: gateway disconnected" )
392
403
}()
393
404
405
+ statsTicker := time .NewTicker (b .statsInterval )
406
+ defer statsTicker .Stop ()
407
+
408
+ // stats publishing loop
409
+ go func () {
410
+ gwIDStr := gatewayID .String ()
411
+
412
+ for {
413
+ select {
414
+ case <- statsTicker .C :
415
+ id , err := uuid .NewV4 ()
416
+ if err != nil {
417
+ log .WithError (err ).Error ("backend/basicstation: new uuid error" )
418
+ continue
419
+ }
420
+
421
+ var rx , rxOK , tx , txOK uint32
422
+ if v , ok := b .statsCache .Get (gwIDStr + ":rx" ); ok {
423
+ rx = v .(uint32 )
424
+ }
425
+ if v , ok := b .statsCache .Get (gwIDStr + ":rxOK" ); ok {
426
+ rxOK = v .(uint32 )
427
+ }
428
+ if v , ok := b .statsCache .Get (gwIDStr + ":tx" ); ok {
429
+ tx = v .(uint32 )
430
+ }
431
+ if v , ok := b .statsCache .Get (gwIDStr + ":txOK" ); ok {
432
+ txOK = v .(uint32 )
433
+ }
434
+
435
+ b .statsCache .Delete (gwIDStr + ":rx" )
436
+ b .statsCache .Delete (gwIDStr + ":rxOK" )
437
+ b .statsCache .Delete (gwIDStr + ":tx" )
438
+ b .statsCache .Delete (gwIDStr + ":txOK" )
439
+
440
+ b .gatewayStatsChan <- gw.GatewayStats {
441
+ GatewayId : gatewayID [:],
442
+ Time : ptypes .TimestampNow (),
443
+ StatsId : id [:],
444
+ RxPacketsReceived : rx ,
445
+ RxPacketsReceivedOk : rxOK ,
446
+ TxPacketsReceived : tx ,
447
+ TxPacketsEmitted : txOK ,
448
+ }
449
+ case <- done :
450
+ return
451
+ }
452
+ }
453
+
454
+ }()
455
+
394
456
// receive data
395
457
for {
396
458
mt , msg , err := c .ReadMessage ()
@@ -447,6 +509,7 @@ func (b *Backend) handleGateway(r *http.Request, c *websocket.Conn) {
447
509
b .handleVersion (gatewayID , pl )
448
510
case structs .UplinkDataFrameMessage :
449
511
// handle uplink
512
+ b .incrementRxStats (gatewayID )
450
513
var pl structs.UplinkDataFrame
451
514
if err := json .Unmarshal (msg , & pl ); err != nil {
452
515
log .WithError (err ).WithFields (log.Fields {
@@ -459,6 +522,7 @@ func (b *Backend) handleGateway(r *http.Request, c *websocket.Conn) {
459
522
b .handleUplinkDataFrame (gatewayID , pl )
460
523
case structs .JoinRequestMessage :
461
524
// handle join-request
525
+ b .incrementRxStats (gatewayID )
462
526
var pl structs.JoinRequest
463
527
if err := json .Unmarshal (msg , & pl ); err != nil {
464
528
log .WithError (err ).WithFields (log.Fields {
@@ -471,6 +535,7 @@ func (b *Backend) handleGateway(r *http.Request, c *websocket.Conn) {
471
535
b .handleJoinRequest (gatewayID , pl )
472
536
case structs .ProprietaryDataFrameMessage :
473
537
// handle proprietary uplink
538
+ b .incrementRxStats (gatewayID )
474
539
var pl structs.UplinkProprietaryFrame
475
540
if err := json .Unmarshal (msg , & pl ); err != nil {
476
541
log .WithError (err ).WithFields (log.Fields {
@@ -483,6 +548,7 @@ func (b *Backend) handleGateway(r *http.Request, c *websocket.Conn) {
483
548
b .handleProprietaryDataFrame (gatewayID , pl )
484
549
case structs .DownlinkTransmittedMessage :
485
550
// handle downlink transmitted
551
+ b .incrementTxOkStats (gatewayID )
486
552
var pl structs.DownlinkTransmitted
487
553
if err := json .Unmarshal (msg , & pl ); err != nil {
488
554
log .WithError (err ).WithFields (log.Fields {
@@ -584,7 +650,10 @@ func (b *Backend) handleDownlinkTransmittedMessage(gatewayID lorawan.EUI64, v st
584
650
}).Error ("backend/basicstation: error converting downlink transmitted to protobuf message" )
585
651
return
586
652
}
587
- txack .DownlinkId = b .diidMap [uint16 (v .DIID )]
653
+
654
+ if v , ok := b .diidCache .Get (fmt .Sprintf ("%d" , v .DIID )); ok {
655
+ txack .DownlinkId = v .([]byte )
656
+ }
588
657
589
658
var downID uuid.UUID
590
659
copy (downID [:], txack .GetDownlinkId ())
@@ -723,3 +792,31 @@ func (b *Backend) websocketWrap(handler func(*http.Request, *websocket.Conn), w
723
792
handler (r , conn )
724
793
done <- struct {}{}
725
794
}
795
+
796
+ func (b * Backend ) incrementRxStats (id lorawan.EUI64 ) {
797
+ idStr := id .String ()
798
+
799
+ if _ , err := b .statsCache .IncrementUint32 (idStr + ":rx" , uint32 (1 )); err != nil {
800
+ b .statsCache .SetDefault (idStr + ":rx" , uint32 (1 ))
801
+ }
802
+
803
+ if _ , err := b .statsCache .IncrementUint32 (idStr + ":rxOK" , uint32 (1 )); err != nil {
804
+ b .statsCache .SetDefault (idStr + ":rxOK" , uint32 (1 ))
805
+ }
806
+ }
807
+
808
+ func (b * Backend ) incrementTxOkStats (id lorawan.EUI64 ) {
809
+ idStr := id .String ()
810
+
811
+ if _ , err := b .statsCache .IncrementUint32 (idStr + "txOK" , uint32 (1 )); err != nil {
812
+ b .statsCache .SetDefault (idStr + ":txOK" , uint32 (1 ))
813
+ }
814
+ }
815
+
816
+ func (b * Backend ) incrementTxStats (id lorawan.EUI64 ) {
817
+ idStr := id .String ()
818
+
819
+ if _ , err := b .statsCache .IncrementUint32 (idStr + "tx" , uint32 (1 )); err != nil {
820
+ b .statsCache .SetDefault (idStr + ":tx" , uint32 (1 ))
821
+ }
822
+ }
0 commit comments