// getNextValidTXPayloadForDRFromQueue returns the next valid TXPayload from the // queue for the given data-rate. When it exceeds the max size, the payload will // be discarded and a notification will be sent to the application. func getNextValidTXPayloadForDRFromQueue(ctx Context, ns models.NodeSession, dataRate int) (*models.TXPayload, error) { for { txPayload, err := storage.GetTXPayloadFromQueue(ctx.RedisPool, ns.DevEUI) if err != nil { if err == common.ErrEmptyQueue { return nil, nil } return nil, err } if len(txPayload.Data) <= common.Band.MaxPayloadSize[dataRate].N { return &txPayload, nil } // the payload exceeded the max payload size for the current data-rate // we'll remove the payload from the queue and notify the application if _, err = storage.ClearInProcessTXPayload(ctx.RedisPool, ns.DevEUI); err != nil { return nil, err } // log a warning log.WithFields(log.Fields{ "dev_eui": ns.DevEUI, "data_rate": dataRate, "frmpayload_size": len(txPayload.Data), "max_frmpayload_size": common.Band.MaxPayloadSize[dataRate].N, "reference": txPayload.Reference, }).Warning("downlink payload max size exceeded") // notify the application err = ctx.Application.SendNotification(ns.AppEUI, ns.DevEUI, models.ErrorNotificationType, models.ErrorPayload{ Reference: txPayload.Reference, DevEUI: ns.DevEUI, Message: fmt.Sprintf("downlink payload max size exceeded (dr: %d, allowed: %d, got: %d)", dataRate, common.Band.MaxPayloadSize[dataRate].N, len(txPayload.Data)), }) if err != nil { return nil, err } } }
func runDataUpTests(ctx Context, devEUI lorawan.EUI64, devAddr lorawan.DevAddr, tests []dataUpTestCase) { for i, test := range tests { Convey(fmt.Sprintf("When testing: %s [%d]", test.Name, i), func() { ctx.Application.(*testApplicationBackend).err = test.ApplicationBackendError for _, pl := range test.TXPayloadQueue { So(storage.AddTXPayloadToQueue(ctx.RedisPool, pl), ShouldBeNil) } for _, mac := range test.TXMACPayloadQueue { So(storage.AddMACPayloadToTXQueue(ctx.RedisPool, mac), ShouldBeNil) } if test.TXMACPayloadInProcess != nil { So(storage.AddTXPayloadToQueue(ctx.RedisPool, *test.TXMACPayloadInProcess), ShouldBeNil) _, err := storage.GetTXPayloadFromQueue(ctx.RedisPool, devEUI) // getting an item from the queue will put it into in-process So(err, ShouldBeNil) } So(test.PHYPayload.EncryptFRMPayload(test.EncryptFRMPayloadKey), ShouldBeNil) So(test.PHYPayload.SetMIC(test.SetMICKey), ShouldBeNil) rxPacket := models.RXPacket{ PHYPayload: test.PHYPayload, RXInfo: test.RXInfo, } So(handleRXPacket(ctx, rxPacket), ShouldResemble, test.ExpectedHandleRXPacketError) Convey("Then the expected rx-info payloads are sent to the network-controller", func() { So(ctx.Controller.(*testControllerBackend).rxInfoPayloadChan, ShouldHaveLength, len(test.ExpectedControllerRXInfoPayloads)) for _, expPL := range test.ExpectedControllerRXInfoPayloads { pl := <-ctx.Controller.(*testControllerBackend).rxInfoPayloadChan So(pl, ShouldResemble, expPL) } }) Convey("Then the expected error payloads are sent to the network-controller", func() { So(ctx.Controller.(*testControllerBackend).errorPayloadChan, ShouldHaveLength, len(test.ExpectedControllerErrorPayloads)) for _, expPL := range test.ExpectedControllerErrorPayloads { pl := <-ctx.Controller.(*testControllerBackend).errorPayloadChan So(pl, ShouldResemble, expPL) } }) Convey("Then the expected mac-commands are received by the network-controller", func() { So(ctx.Controller.(*testControllerBackend).rxMACPayloadChan, ShouldHaveLength, len(test.ExpectedControllerRXMACPayloads)) for _, expPl := range test.ExpectedControllerRXMACPayloads { pl := <-ctx.Controller.(*testControllerBackend).rxMACPayloadChan So(pl, ShouldResemble, expPl) } }) Convey("Then the expected rx-payloads are received by the application-backend", func() { So(ctx.Application.(*testApplicationBackend).rxPayloadChan, ShouldHaveLength, len(test.ExpectedApplicationRXPayloads)) for _, expPL := range test.ExpectedApplicationRXPayloads { pl := <-ctx.Application.(*testApplicationBackend).rxPayloadChan So(pl, ShouldResemble, expPL) } }) Convey("Then the expected notifications are received by the application-backend", func() { So(ctx.Application.(*testApplicationBackend).notificationPayloadChan, ShouldHaveLength, len(test.ExpectedApplicationNotifications)) for _, expPL := range test.ExpectedApplicationNotifications { pl := <-ctx.Application.(*testApplicationBackend).notificationPayloadChan So(pl, ShouldResemble, expPL) } }) Convey("Then the expected tx-packets are received by the gateway (FRMPayload decrypted and MIC ignored)", func() { So(ctx.Gateway.(*testGatewayBackend).txPacketChan, ShouldHaveLength, len(test.ExpectedGatewayTXPackets)) for _, expPL := range test.ExpectedGatewayTXPackets { pl := <-ctx.Gateway.(*testGatewayBackend).txPacketChan So(pl.PHYPayload.DecryptFRMPayload(test.DecryptExpectedFRMPayloadKey), ShouldBeNil) expPL.PHYPayload.MIC = pl.PHYPayload.MIC So(pl, ShouldResemble, expPL) } }) Convey("Then the frame-counters are as expected", func() { ns, err := storage.GetNodeSessionByDevEUI(ctx.RedisPool, devEUI) So(err, ShouldBeNil) So(ns.FCntDown, ShouldEqual, test.ExpectedFCntDown) So(ns.FCntUp, ShouldEqual, test.ExpectedFCntUp) }) Convey("Then the MACPayload tx-queue is as expected", func() { macQueue, err := storage.ReadMACPayloadTXQueue(ctx.RedisPool, devAddr) So(err, ShouldBeNil) So(macQueue, ShouldResemble, test.ExpectedTXMACPayloadQueue) }) Convey("Then the next TXPayload is as expected", func() { txPL, err := storage.GetTXPayloadFromQueue(ctx.RedisPool, devEUI) if test.ExpectedGetTXPayloadFromQueue == nil { So(err, ShouldResemble, common.ErrEmptyQueue) } else { So(err, ShouldBeNil) So(txPL, ShouldResemble, *test.ExpectedGetTXPayloadFromQueue) } }) }) } }