func TestHandleActivation(t *testing.T) { a := New(t) gtwID := "eui-0102030405060708" devEUI := types.DevEUI([8]byte{0, 1, 2, 3, 4, 5, 6, 7}) appEUI := types.AppEUI([8]byte{0, 1, 2, 3, 4, 5, 6, 7}) b := getTestBroker(t) b.ns.EXPECT().PrepareActivation(gomock.Any(), gomock.Any()).Return(&pb_broker.DeduplicatedDeviceActivationRequest{ Payload: []byte{}, DevEui: &devEUI, AppEui: &appEUI, AppId: "appid", DevId: "devid", GatewayMetadata: []*gateway.RxMetadata{ &gateway.RxMetadata{Snr: 1.2, GatewayId: gtwID}, }, ProtocolMetadata: &protocol.RxMetadata{}, }, nil) b.discovery.EXPECT().GetAllHandlersForAppID("appid").Return([]*pb_discovery.Announcement{}, nil) res, err := b.HandleActivation(&pb_broker.DeviceActivationRequest{ Payload: []byte{}, DevEui: &devEUI, AppEui: &appEUI, GatewayMetadata: &gateway.RxMetadata{Snr: 1.2, GatewayId: gtwID}, ProtocolMetadata: &protocol.RxMetadata{}, }) a.So(err, ShouldNotBeNil) a.So(res, ShouldBeNil) b.ctrl.Finish() // TODO: Integration test with Handler }
// JoinRequestPayloadFromPayload creates a new JoinRequestPayload from a lorawan.Payload func JoinRequestPayloadFromPayload(payload lorawan.Payload) (request JoinRequestPayload) { if payload, ok := payload.(*lorawan.JoinRequestPayload); ok { request.AppEui = types.AppEUI(payload.AppEUI) request.DevEui = types.DevEUI(payload.DevEUI) request.DevNonce = types.DevNonce(payload.DevNonce) } return }
func TestHandleActivate(t *testing.T) { a := New(t) ns := &networkServer{ devices: device.NewRedisDeviceStore(GetRedisClient(), "test-handle-activate"), } ns.InitStatus() dev := &device.Device{ AppEUI: types.AppEUI(getEUI(0, 0, 0, 0, 0, 0, 3, 1)), DevEUI: types.DevEUI(getEUI(0, 0, 0, 0, 0, 0, 3, 1)), } a.So(ns.devices.Set(dev), ShouldBeNil) defer func() { ns.devices.Delete(types.AppEUI(getEUI(0, 0, 0, 0, 0, 0, 3, 1)), types.DevEUI(getEUI(0, 0, 0, 0, 0, 0, 3, 1))) }() _, err := ns.HandleActivate(&pb_handler.DeviceActivationResponse{}) a.So(err, ShouldNotBeNil) _, err = ns.HandleActivate(&pb_handler.DeviceActivationResponse{ ActivationMetadata: &pb_protocol.ActivationMetadata{}, }) a.So(err, ShouldNotBeNil) devAddr := getDevAddr(0, 0, 3, 1) var nwkSKey types.NwkSKey copy(nwkSKey[:], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1}) appEUI := types.AppEUI(getEUI(0, 0, 0, 0, 0, 0, 3, 1)) devEUI := types.DevEUI(getEUI(0, 0, 0, 0, 0, 0, 3, 1)) _, err = ns.HandleActivate(&pb_handler.DeviceActivationResponse{ ActivationMetadata: &pb_protocol.ActivationMetadata{Protocol: &pb_protocol.ActivationMetadata_Lorawan{ Lorawan: &pb_lorawan.ActivationMetadata{ AppEui: &appEUI, DevEui: &devEUI, DevAddr: &devAddr, NwkSKey: &nwkSKey, }, }}, }) a.So(err, ShouldBeNil) }
func buildConversionDownlink() (*pb_broker.DownlinkMessage, *types.DownlinkMessage) { appEUI := types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) devEUI := types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) ttnDown := &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, } appDown := &types.DownlinkMessage{ FPort: 1, AppID: "AppID-1", DevID: "DevID-1", PayloadFields: map[string]interface{}{"temperature": 30}, // We want to "build" the payload with the content of the fields } return ttnDown, appDown }
func TestHandleDownlink(t *testing.T) { a := New(t) ns := &networkServer{ devices: device.NewRedisDeviceStore(GetRedisClient(), "test-handle-downlink"), } ns.InitStatus() appEUI := types.AppEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)) devEUI := types.DevEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)) devAddr := getDevAddr(1, 2, 3, 4) // Device Not Found message := &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{}, } _, err := ns.HandleDownlink(message) a.So(err, ShouldNotBeNil) ns.devices.Set(&device.Device{ DevAddr: devAddr, AppEUI: appEUI, DevEUI: devEUI, }) defer func() { ns.devices.Delete(appEUI, devEUI) }() // Invalid Payload message = &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{}, } _, err = ns.HandleDownlink(message) a.So(err, ShouldNotBeNil) fPort := uint8(3) phy := lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FPort: &fPort, FHDR: lorawan.FHDR{ FCtrl: lorawan.FCtrl{ ACK: true, }, }, }, } bytes, _ := phy.MarshalBinary() message = &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: bytes, } res, err := ns.HandleDownlink(message) a.So(err, ShouldBeNil) var phyPayload lorawan.PHYPayload phyPayload.UnmarshalBinary(res.Payload) macPayload, _ := phyPayload.MACPayload.(*lorawan.MACPayload) a.So(*macPayload.FPort, ShouldEqual, 3) a.So(macPayload.FHDR.DevAddr, ShouldEqual, lorawan.DevAddr{1, 2, 3, 4}) a.So(macPayload.FHDR.FCnt, ShouldEqual, 0) // The first Frame counter is zero a.So(phyPayload.MIC, ShouldNotEqual, [4]byte{0, 0, 0, 0}) // MIC should be set, we'll check it with actual examples in the integration test dev, _ := ns.devices.Get(appEUI, devEUI) a.So(dev.FCntDown, ShouldEqual, 1) }
func TestHandlePrepareActivation(t *testing.T) { a := New(t) ns := &networkServer{ netID: [3]byte{0x00, 0x00, 0x13}, prefixes: map[types.DevAddrPrefix][]string{ types.DevAddrPrefix{DevAddr: [4]byte{0x26, 0x00, 0x00, 0x00}, Length: 7}: []string{ "otaa", "local", }, }, devices: device.NewRedisDeviceStore(GetRedisClient(), "test-handle-prepare-activation"), } appEUI := types.AppEUI(getEUI(2, 2, 3, 4, 5, 6, 7, 8)) devEUI := types.DevEUI(getEUI(2, 2, 3, 4, 5, 6, 7, 8)) // Device not registered resp, err := ns.HandlePrepareActivation(&pb_broker.DeduplicatedDeviceActivationRequest{ ActivationMetadata: &pb_protocol.ActivationMetadata{Protocol: &pb_protocol.ActivationMetadata_Lorawan{ Lorawan: &pb_lorawan.ActivationMetadata{ CfList: &pb_lorawan.CFList{Freq: []uint32{867100000, 867300000, 867500000, 867700000, 867900000}}, }, }}, ResponseTemplate: &pb_broker.DeviceActivationResponse{}, }) a.So(err, ShouldNotBeNil) dev := &device.Device{AppEUI: appEUI, DevEUI: devEUI, Options: device.Options{ ActivationConstraints: "private", }} a.So(ns.devices.Set(dev), ShouldBeNil) defer func() { ns.devices.Delete(appEUI, devEUI) }() // Constrained Device resp, err = ns.HandlePrepareActivation(&pb_broker.DeduplicatedDeviceActivationRequest{ DevEui: &devEUI, AppEui: &appEUI, ActivationMetadata: &pb_protocol.ActivationMetadata{Protocol: &pb_protocol.ActivationMetadata_Lorawan{ Lorawan: &pb_lorawan.ActivationMetadata{ CfList: &pb_lorawan.CFList{Freq: []uint32{867100000, 867300000, 867500000, 867700000, 867900000}}, }, }}, ResponseTemplate: &pb_broker.DeviceActivationResponse{}, }) a.So(err, ShouldNotBeNil) dev.StartUpdate() dev.Options = device.Options{} a.So(ns.devices.Set(dev), ShouldBeNil) // Device registered resp, err = ns.HandlePrepareActivation(&pb_broker.DeduplicatedDeviceActivationRequest{ DevEui: &devEUI, AppEui: &appEUI, ActivationMetadata: &pb_protocol.ActivationMetadata{Protocol: &pb_protocol.ActivationMetadata_Lorawan{ Lorawan: &pb_lorawan.ActivationMetadata{ CfList: &pb_lorawan.CFList{Freq: []uint32{867100000, 867300000, 867500000, 867700000, 867900000}}, }, }}, ResponseTemplate: &pb_broker.DeviceActivationResponse{}, }) a.So(err, ShouldBeNil) devAddr := resp.ActivationMetadata.GetLorawan().DevAddr a.So(devAddr.IsEmpty(), ShouldBeFalse) a.So(devAddr[0]&254, ShouldEqual, 19<<1) // 7 MSB should be NetID var resPHY lorawan.PHYPayload resPHY.UnmarshalBinary(resp.ResponseTemplate.Payload) resMAC, _ := resPHY.MACPayload.(*lorawan.DataPayload) joinAccept := &lorawan.JoinAcceptPayload{} joinAccept.UnmarshalBinary(false, resMAC.Bytes) a.So(joinAccept.DevAddr[0]&254, ShouldEqual, 19<<1) a.So(*joinAccept.CFList, ShouldEqual, lorawan.CFList{867100000, 867300000, 867500000, 867700000, 867900000}) }
func TestHandleUplink(t *testing.T) { a := New(t) ns := &networkServer{ devices: device.NewRedisDeviceStore(GetRedisClient(), "ns-test-handle-uplink"), } ns.InitStatus() appEUI := types.AppEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)) devEUI := types.DevEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)) devAddr := getDevAddr(1, 2, 3, 4) // Device Not Found message := &pb_broker.DeduplicatedUplinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{}, } _, err := ns.HandleUplink(message) a.So(err, ShouldNotBeNil) ns.devices.Set(&device.Device{ DevAddr: devAddr, AppEUI: appEUI, DevEUI: devEUI, }) defer func() { ns.devices.Delete(appEUI, devEUI) }() // Invalid Payload message = &pb_broker.DeduplicatedUplinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{}, } _, err = ns.HandleUplink(message) a.So(err, ShouldNotBeNil) phy := lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: lorawan.DevAddr([4]byte{1, 2, 3, 4}), FCnt: 1, FCtrl: lorawan.FCtrl{ ADR: true, ADRACKReq: true, }, FOpts: []lorawan.MACCommand{ lorawan.MACCommand{CID: lorawan.LinkCheckReq}, }, }, }, } bytes, _ := phy.MarshalBinary() // Valid Uplink message = &pb_broker.DeduplicatedUplinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: bytes, ResponseTemplate: &pb_broker.DownlinkMessage{}, GatewayMetadata: []*pb_gateway.RxMetadata{ &pb_gateway.RxMetadata{}, }, ProtocolMetadata: &pb_protocol.RxMetadata{Protocol: &pb_protocol.RxMetadata_Lorawan{ Lorawan: &pb_lorawan.Metadata{ DataRate: "SF7BW125", }, }}, } res, err := ns.HandleUplink(message) a.So(err, ShouldBeNil) a.So(res.ResponseTemplate, ShouldNotBeNil) // LoRaWAN: Unmarshal var phyPayload lorawan.PHYPayload phyPayload.UnmarshalBinary(res.ResponseTemplate.Payload) macPayload, _ := phyPayload.MACPayload.(*lorawan.MACPayload) // ResponseTemplate DevAddr should match a.So([4]byte(macPayload.FHDR.DevAddr), ShouldEqual, [4]byte(devAddr)) // ResponseTemplate should ACK the ADRACKReq a.So(macPayload.FHDR.FCtrl.ACK, ShouldBeTrue) a.So(macPayload.FHDR.FOpts, ShouldHaveLength, 1) a.So(macPayload.FHDR.FOpts[0].Payload, ShouldResemble, &lorawan.LinkCheckAnsPayload{GwCnt: 1, Margin: 7}) // Frame Counter should have been updated dev, _ := ns.devices.Get(appEUI, devEUI) a.So(dev.FCntUp, ShouldEqual, 1) a.So(time.Now().Sub(dev.LastSeen), ShouldBeLessThan, 1*time.Second) }
func TestHandleUplink(t *testing.T) { a := New(t) var err error var wg WaitGroup appEUI := types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) appID := "appid" devEUI := types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) devID := "devid" h := &handler{ Component: &component.Component{Ctx: GetLogger(t, "TestHandleUplink")}, devices: device.NewRedisDeviceStore(GetRedisClient(), "handler-test-handle-uplink"), applications: application.NewRedisApplicationStore(GetRedisClient(), "handler-test-handle-uplink"), } h.InitStatus() dev := &device.Device{ AppID: appID, DevID: devID, AppEUI: appEUI, DevEUI: devEUI, } h.devices.Set(dev) defer func() { h.devices.Delete(appID, devID) }() h.applications.Set(&application.Application{ AppID: appID, }) defer func() { h.applications.Delete(appID) }() h.mqttUp = make(chan *types.UplinkMessage) h.mqttEvent = make(chan *types.DeviceEvent, 10) h.downlink = make(chan *pb_broker.DownlinkMessage) uplink, _ := buildLorawanUplink([]byte{0x40, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x0A, 0x4D, 0xDA, 0x23, 0x99, 0x61, 0xD4}) downlinkEmpty := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x21, 0xEA, 0x8B, 0x0E} downlinkACK := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x20, 0x00, 0x00, 0x0A, 0x3B, 0x3F, 0x77, 0x0B} downlinkMAC := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x05, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x0A, 0x4D, 0x11, 0x55, 0x01} expected := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x66, 0xE6, 0x1D, 0x49, 0x82, 0x84} downlink := &pb_broker.DownlinkMessage{ DownlinkOption: &pb_broker.DownlinkOption{ ProtocolConfig: &pb_protocol.TxConfiguration{Protocol: &pb_protocol.TxConfiguration_Lorawan{Lorawan: &pb_lorawan.TxConfiguration{ FCnt: 0, }}}, }, } // Test Uplink, no downlink option available wg.Add(1) go func() { <-h.mqttUp wg.Done() }() err = h.HandleUplink(uplink) a.So(err, ShouldBeNil) wg.WaitFor(50 * time.Millisecond) uplink.ResponseTemplate = downlink // Test Uplink, no downlink needed wg.Add(1) go func() { <-h.mqttUp wg.Done() }() downlink.Payload = downlinkEmpty err = h.HandleUplink(uplink) a.So(err, ShouldBeNil) wg.WaitFor(50 * time.Millisecond) // Test Uplink, ACK downlink needed wg.Add(2) go func() { <-h.mqttUp wg.Done() }() go func() { <-h.downlink wg.Done() }() downlink.Payload = downlinkACK err = h.HandleUplink(uplink) a.So(err, ShouldBeNil) wg.WaitFor(50 * time.Millisecond) // Test Uplink, MAC downlink needed wg.Add(2) go func() { <-h.mqttUp wg.Done() }() go func() { <-h.downlink wg.Done() }() downlink.Payload = downlinkMAC err = h.HandleUplink(uplink) a.So(err, ShouldBeNil) wg.WaitFor(50 * time.Millisecond) dev.StartUpdate() dev.NextDownlink = &types.DownlinkMessage{ PayloadRaw: []byte{0xaa, 0xbc}, } // Test Uplink, Data downlink needed h.devices.Set(dev) wg.Add(2) go func() { <-h.mqttUp wg.Done() }() go func() { dl := <-h.downlink a.So(dl.Payload, ShouldResemble, expected) wg.Done() }() downlink.Payload = downlinkEmpty err = h.HandleUplink(uplink) a.So(err, ShouldBeNil) wg.WaitFor(50 * time.Millisecond) dev, _ = h.devices.Get(appID, devID) a.So(dev.NextDownlink, ShouldBeNil) }
func TestDeviceStore(t *testing.T) { a := New(t) NewRedisDeviceStore(GetRedisClient(), "") s := NewRedisDeviceStore(GetRedisClient(), "handler-test-device-store") // Get non-existing dev, err := s.Get("AppID-1", "DevID-1") a.So(err, ShouldNotBeNil) a.So(dev, ShouldBeNil) devs, err := s.ListForApp("AppID-1") a.So(err, ShouldBeNil) a.So(devs, ShouldHaveLength, 0) // Create err = s.Set(&Device{ DevAddr: types.DevAddr([4]byte{0, 0, 0, 1}), DevEUI: types.DevEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), AppEUI: types.AppEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), AppID: "AppID-1", DevID: "DevID-1", }) a.So(err, ShouldBeNil) defer func() { s.Delete("AppID-1", "DevID-1") }() // Get existing dev, err = s.Get("AppID-1", "DevID-1") a.So(err, ShouldBeNil) a.So(dev, ShouldNotBeNil) devs, err = s.ListForApp("AppID-1") a.So(err, ShouldBeNil) a.So(devs, ShouldHaveLength, 1) // Create extra and update dev = &Device{ DevAddr: types.DevAddr([4]byte{0, 0, 0, 2}), DevEUI: types.DevEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 2}), AppEUI: types.AppEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), AppID: "AppID-1", DevID: "DevID-2", } err = s.Set(dev) a.So(err, ShouldBeNil) err = s.Set(&Device{ old: dev, DevAddr: types.DevAddr([4]byte{0, 0, 0, 3}), DevEUI: types.DevEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 3}), AppEUI: types.AppEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 2}), AppID: "AppID-1", DevID: "DevID-2", }) a.So(err, ShouldBeNil) dev, err = s.Get("AppID-1", "DevID-2") a.So(err, ShouldBeNil) a.So(dev, ShouldNotBeNil) a.So(dev.DevEUI, ShouldEqual, types.DevEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 3})) defer func() { s.Delete("AppID-1", "DevID-2") }() // List devices, err := s.List() a.So(err, ShouldBeNil) a.So(devices, ShouldHaveLength, 2) // Delete err = s.Delete("AppID-1", "DevID-1") a.So(err, ShouldBeNil) // Get deleted dev, err = s.Get("AppID-1", "DevID-1") a.So(err, ShouldNotBeNil) a.So(dev, ShouldBeNil) devs, err = s.ListForApp("AppID-1") a.So(err, ShouldBeNil) a.So(devs, ShouldHaveLength, 1) }
func TestMarshalUnmarshalPayload(t *testing.T) { a := New(t) var subjects []payloadMarshalerUnmarshaler // Do nothing when message and payload are nil subjects = []payloadMarshalerUnmarshaler{ &UplinkMessage{}, &DownlinkMessage{}, &DeviceActivationRequest{}, } for _, sub := range subjects { a.So(sub.MarshalPayload(), ShouldEqual, nil) a.So(sub.UnmarshalPayload(), ShouldEqual, nil) } rxMeta := &pb_protocol.RxMetadata{Protocol: &pb_protocol.RxMetadata_Lorawan{Lorawan: &pb_lorawan.Metadata{}}} txConf := &pb_protocol.TxConfiguration{Protocol: &pb_protocol.TxConfiguration_Lorawan{Lorawan: &pb_lorawan.TxConfiguration{}}} macMsg := &pb_protocol.Message{Protocol: &pb_protocol.Message_Lorawan{Lorawan: &pb_lorawan.Message{ MHDR: pb_lorawan.MHDR{ Major: 1, MType: pb_lorawan.MType_UNCONFIRMED_UP, }, Payload: &pb_lorawan.Message_MacPayload{MacPayload: &pb_lorawan.MACPayload{ FHDR: pb_lorawan.FHDR{ DevAddr: types.DevAddr([4]byte{1, 2, 3, 4}), FCnt: 1, }, }}, Mic: []byte{1, 2, 3, 4}, }}} macBin := []byte{65, 4, 3, 2, 1, 0, 1, 0, 0, 1, 2, 3, 4} joinReqMsg := &pb_protocol.Message{Protocol: &pb_protocol.Message_Lorawan{Lorawan: &pb_lorawan.Message{ MHDR: pb_lorawan.MHDR{ Major: 1, MType: pb_lorawan.MType_JOIN_REQUEST, }, Payload: &pb_lorawan.Message_JoinRequestPayload{JoinRequestPayload: &pb_lorawan.JoinRequestPayload{ AppEui: types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}), DevEui: types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}), DevNonce: types.DevNonce([2]byte{1, 2}), }}, Mic: []byte{1, 2, 3, 4}, }}} joinReqBin := []byte{1, 8, 7, 6, 5, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1, 2, 1, 1, 2, 3, 4} // Only Marshal subjects = []payloadMarshalerUnmarshaler{ &UplinkMessage{ ProtocolMetadata: rxMeta, Message: macMsg, }, &DownlinkMessage{ ProtocolConfiguration: txConf, Message: macMsg, }, &DeviceActivationRequest{ ProtocolMetadata: rxMeta, Message: joinReqMsg, }, } for _, sub := range subjects { a.So(sub.UnmarshalPayload(), ShouldEqual, nil) a.So(sub.MarshalPayload(), ShouldEqual, nil) } // Only Unmarshal subjects = []payloadMarshalerUnmarshaler{ &UplinkMessage{ ProtocolMetadata: rxMeta, Payload: macBin, }, &DownlinkMessage{ ProtocolConfiguration: txConf, Payload: macBin, }, &DeviceActivationRequest{ ProtocolMetadata: rxMeta, Payload: joinReqBin, }, } for _, sub := range subjects { a.So(sub.MarshalPayload(), ShouldEqual, nil) a.So(sub.UnmarshalPayload(), ShouldEqual, nil) } }
func TestHandleGetDevices(t *testing.T) { a := New(t) ns := &networkServer{ devices: device.NewRedisDeviceStore(GetRedisClient(), "ns-test-handle-get-devices"), } nwkSKey := types.NwkSKey{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8} // No Devices devAddr1 := getDevAddr(1, 2, 3, 4) res, err := ns.HandleGetDevices(&pb.DevicesRequest{ DevAddr: &devAddr1, FCnt: 5, }) a.So(err, ShouldBeNil) a.So(res.Results, ShouldBeEmpty) // Matching Device ns.devices.Set(&device.Device{ DevAddr: getDevAddr(1, 2, 3, 4), AppEUI: types.AppEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)), DevEUI: types.DevEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)), NwkSKey: nwkSKey, FCntUp: 5, }) defer func() { ns.devices.Delete(types.AppEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)), types.DevEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8))) }() res, err = ns.HandleGetDevices(&pb.DevicesRequest{ DevAddr: &devAddr1, FCnt: 5, }) a.So(err, ShouldBeNil) a.So(res.Results, ShouldHaveLength, 1) // Non-Matching DevAddr devAddr2 := getDevAddr(5, 6, 7, 8) res, err = ns.HandleGetDevices(&pb.DevicesRequest{ DevAddr: &devAddr2, FCnt: 5, }) a.So(err, ShouldBeNil) a.So(res.Results, ShouldHaveLength, 0) // Non-Matching FCnt res, err = ns.HandleGetDevices(&pb.DevicesRequest{ DevAddr: &devAddr1, FCnt: 4, }) a.So(err, ShouldBeNil) a.So(res.Results, ShouldHaveLength, 0) // Non-Matching FCnt, but FCnt Check Disabled ns.devices.Set(&device.Device{ DevAddr: getDevAddr(5, 6, 7, 8), AppEUI: types.AppEUI(getEUI(5, 6, 7, 8, 1, 2, 3, 4)), DevEUI: types.DevEUI(getEUI(5, 6, 7, 8, 1, 2, 3, 4)), NwkSKey: nwkSKey, FCntUp: 5, Options: device.Options{ DisableFCntCheck: true, }, }) defer func() { ns.devices.Delete(types.AppEUI(getEUI(5, 6, 7, 8, 1, 2, 3, 4)), types.DevEUI(getEUI(5, 6, 7, 8, 1, 2, 3, 4))) }() res, err = ns.HandleGetDevices(&pb.DevicesRequest{ DevAddr: &devAddr2, FCnt: 4, }) a.So(err, ShouldBeNil) a.So(res.Results, ShouldHaveLength, 1) // 32 Bit Frame Counter (A) ns.devices.Set(&device.Device{ DevAddr: getDevAddr(2, 2, 3, 4), AppEUI: types.AppEUI(getEUI(2, 2, 3, 4, 5, 6, 7, 8)), DevEUI: types.DevEUI(getEUI(2, 2, 3, 4, 5, 6, 7, 8)), NwkSKey: nwkSKey, FCntUp: 5 + (2 << 16), Options: device.Options{ Uses32BitFCnt: true, }, }) defer func() { ns.devices.Delete(types.AppEUI(getEUI(2, 2, 3, 4, 5, 6, 7, 8)), types.DevEUI(getEUI(2, 2, 3, 4, 5, 6, 7, 8))) }() devAddr3 := getDevAddr(2, 2, 3, 4) res, err = ns.HandleGetDevices(&pb.DevicesRequest{ DevAddr: &devAddr3, FCnt: 5, }) a.So(err, ShouldBeNil) a.So(res.Results, ShouldHaveLength, 1) // 32 Bit Frame Counter (B) ns.devices.Set(&device.Device{ DevAddr: getDevAddr(2, 2, 3, 5), AppEUI: types.AppEUI(getEUI(2, 2, 3, 4, 5, 3, 7, 8)), DevEUI: types.DevEUI(getEUI(2, 2, 3, 4, 5, 3, 7, 8)), NwkSKey: nwkSKey, FCntUp: (2 << 16) - 1, Options: device.Options{ Uses32BitFCnt: true, }, }) defer func() { ns.devices.Delete(types.AppEUI(getEUI(2, 2, 3, 4, 5, 3, 7, 8)), types.DevEUI(getEUI(2, 2, 3, 4, 5, 3, 7, 8))) }() devAddr4 := getDevAddr(2, 2, 3, 5) res, err = ns.HandleGetDevices(&pb.DevicesRequest{ DevAddr: &devAddr4, FCnt: 5, }) a.So(err, ShouldBeNil) a.So(res.Results, ShouldHaveLength, 1) }
func TestHandleDownlink(t *testing.T) { a := New(t) var err error var wg WaitGroup appID := "app2" devID := "dev2" appEUI := types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) devEUI := types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) h := &handler{ Component: &component.Component{Ctx: GetLogger(t, "TestHandleDownlink")}, devices: device.NewRedisDeviceStore(GetRedisClient(), "handler-test-handle-downlink"), applications: application.NewRedisApplicationStore(GetRedisClient(), "handler-test-enqueue-downlink"), downlink: make(chan *pb_broker.DownlinkMessage), mqttEvent: make(chan *types.DeviceEvent, 10), } h.InitStatus() // Neither payload nor Fields provided : ERROR err = h.HandleDownlink(&types.DownlinkMessage{ AppID: appID, DevID: devID, }, &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, }) a.So(err, ShouldNotBeNil) h.devices.Set(&device.Device{ AppID: appID, DevID: devID, }) defer func() { h.devices.Delete(appID, devID) }() err = h.HandleDownlink(&types.DownlinkMessage{ AppID: appID, DevID: devID, }, &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0}, }) a.So(err, ShouldBeNil) // Payload provided wg.Add(1) go func() { dl := <-h.downlink a.So(dl.Payload, ShouldNotBeEmpty) wg.Done() }() err = h.HandleDownlink(&types.DownlinkMessage{ AppID: appID, DevID: devID, PayloadRaw: []byte{0xAA, 0xBC}, }, &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0}, DownlinkOption: &pb_broker.DownlinkOption{}, }) a.So(err, ShouldBeNil) wg.WaitFor(100 * time.Millisecond) // Both Payload and Fields provided h.applications.Set(&application.Application{ AppID: appID, Encoder: `function Encoder (payload){ return [96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0] }`, }) defer func() { h.applications.Delete(appID) }() jsonFields := map[string]interface{}{"temperature": 11} err = h.HandleDownlink(&types.DownlinkMessage{ FPort: 1, AppID: appID, DevID: devID, PayloadFields: jsonFields, PayloadRaw: []byte{0xAA, 0xBC}, }, &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0}, }) a.So(err, ShouldNotBeNil) // JSON Fields provided wg.Add(1) go func() { dl := <-h.downlink a.So(dl.Payload, ShouldNotBeEmpty) wg.Done() }() err = h.HandleDownlink(&types.DownlinkMessage{ FPort: 1, AppID: appID, DevID: devID, PayloadFields: jsonFields, }, &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0}, DownlinkOption: &pb_broker.DownlinkOption{}, }) a.So(err, ShouldBeNil) wg.WaitFor(100 * time.Millisecond) }
func (r *router) HandleUplink(gatewayID string, uplink *pb.UplinkMessage) (err error) { ctx := r.Ctx.WithField("GatewayID", gatewayID) start := time.Now() defer func() { if err != nil { ctx.WithError(err).Warn("Could not handle uplink") } }() r.status.uplink.Mark(1) // LoRaWAN: Unmarshal var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(uplink.Payload) if err != nil { return err } if phyPayload.MHDR.MType == lorawan.JoinRequest { joinRequestPayload, ok := phyPayload.MACPayload.(*lorawan.JoinRequestPayload) if !ok { return errors.NewErrInvalidArgument("Join Request", "does not contain a JoinRequest payload") } devEUI := types.DevEUI(joinRequestPayload.DevEUI) appEUI := types.AppEUI(joinRequestPayload.AppEUI) ctx.WithFields(log.Fields{ "DevEUI": devEUI, "AppEUI": appEUI, }).Debug("Handle Uplink as Activation") r.HandleActivation(gatewayID, &pb.DeviceActivationRequest{ Payload: uplink.Payload, DevEui: &devEUI, AppEui: &appEUI, ProtocolMetadata: uplink.ProtocolMetadata, GatewayMetadata: uplink.GatewayMetadata, }) return nil } if lorawan := uplink.ProtocolMetadata.GetLorawan(); lorawan != nil { ctx = ctx.WithField("Modulation", lorawan.Modulation.String()) if lorawan.Modulation == pb_lorawan.Modulation_LORA { ctx = ctx.WithField("DataRate", lorawan.DataRate) } else { ctx = ctx.WithField("BitRate", lorawan.BitRate) } } if gateway := uplink.GatewayMetadata; gateway != nil { ctx = ctx.WithFields(log.Fields{ "Frequency": gateway.Frequency, "RSSI": gateway.Rssi, "SNR": gateway.Snr, }) } macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload) if !ok { return errors.NewErrInvalidArgument("Uplink", "does not contain a MAC payload") } devAddr := types.DevAddr(macPayload.FHDR.DevAddr) ctx = ctx.WithFields(log.Fields{ "DevAddr": devAddr, "FCnt": macPayload.FHDR.FCnt, }) gateway := r.getGateway(gatewayID) if err = gateway.HandleUplink(uplink); err != nil { return err } var downlinkOptions []*pb_broker.DownlinkOption if gateway.Schedule.IsActive() { downlinkOptions = r.buildDownlinkOptions(uplink, false, gateway) } ctx = ctx.WithField("DownlinkOptions", len(downlinkOptions)) // Find Broker brokers, err := r.Discovery.GetAllBrokersForDevAddr(devAddr) if err != nil { return err } if len(brokers) == 0 { ctx.Debug("No brokers to forward message to") return nil } ctx = ctx.WithField("NumBrokers", len(brokers)) // Forward to all brokers for _, broker := range brokers { broker, err := r.getBroker(broker) if err != nil { continue } broker.uplink <- &pb_broker.UplinkMessage{ Payload: uplink.Payload, ProtocolMetadata: uplink.ProtocolMetadata, GatewayMetadata: uplink.GatewayMetadata, DownlinkOptions: downlinkOptions, } } ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled uplink") return nil }