Example #1
0
// 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)
				}
			})
		})
	}
}