Пример #1
0
        public async Task When_Payload_Has_Invalid_Mic_Should_Not_Send_Messages(int searchDevicesDelayMs)
        {
            // Setup
            const string wrongSKey       = "00000000000000000000000000EEDDFF";
            var          simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1));

            var searchResult = new SearchDevicesResult(new IoTHubDeviceInfo(simulatedDevice.DevAddr, simulatedDevice.DevEUI, "1321").AsList());

            if (searchDevicesDelayMs > 0)
            {
                this.LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr))
                .ReturnsAsync(searchResult, TimeSpan.FromMilliseconds(searchDevicesDelayMs));
            }
            else
            {
                this.LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr))
                .ReturnsAsync(searchResult);
            }

            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(TestUtils.CreateABPTwin(simulatedDevice));

            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewMemoryCache(), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageProcessor = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            // Send request #1
            var payload1 = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 2);
            var request1 = this.CreateWaitableRequest(payload1.SerializeUplink(simulatedDevice.AppSKey, wrongSKey).Rxpk[0]);

            messageProcessor.DispatchRequest(request1);
            Assert.True(await request1.WaitCompleteAsync());
            Assert.Null(request1.ResponseDownlink);
            Assert.True(request1.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.NotMatchingDeviceByMicCheck, request1.ProcessingFailedReason);

            await Task.Delay(2000);

            Assert.Single(deviceRegistry.InternalGetCachedDevicesForDevAddr(simulatedDevice.DevAddr));

            // Send request #2
            var payload2 = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 3);
            var request2 = this.CreateWaitableRequest(payload2.SerializeUplink(simulatedDevice.AppSKey, wrongSKey).Rxpk[0]);

            messageProcessor.DispatchRequest(request2);
            Assert.True(await request2.WaitCompleteAsync());
            Assert.Null(request2.ResponseDownlink);
            Assert.True(request2.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.NotMatchingDeviceByMicCheck, request2.ProcessingFailedReason);
            Assert.Single(deviceRegistry.InternalGetCachedDevicesForDevAddr(simulatedDevice.DevAddr));

            this.LoRaDeviceApi.VerifyAll();
            this.LoRaDeviceClient.VerifyAll();

            this.LoRaDeviceApi.Verify(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr), Times.Exactly(1));
        }
        public void When_Cache_Clear_Is_Called_Should_Removed_Cached_Devices(string deviceGatewayID)
        {
            const int deviceCount = 10;
            var deviceList = new HashSet<LoRaDevice>();

            var apiService = new Mock<LoRaDeviceAPIServiceBase>();
            var deviceFactory = new TestLoRaDeviceFactory(this.loRaDeviceClient.Object);
            var target = new LoRaDeviceRegistry(this.serverConfiguration, this.cache, apiService.Object, deviceFactory);

            var getTwinMockSequence = this.loRaDeviceClient.SetupSequence(x => x.GetTwinAsync());

            for (var deviceID = 1; deviceID <= deviceCount; ++deviceID)
            {
                var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice((uint)deviceID, gatewayID: deviceGatewayID));
                var dict = target.InternalGetCachedDevicesForDevAddr(simulatedDevice.DevAddr);
                var device = TestUtils.CreateFromSimulatedDevice(simulatedDevice, this.loRaDeviceClient.Object);
                deviceList.Add(device);
                dict.TryAdd(simulatedDevice.DevEUI, device);
            }

            Assert.Equal(deviceCount, this.cache.Count);

            // Device was searched by DevAddr
            apiService.VerifyAll();

            // Device was created by factory
            this.loraDeviceFactoryMock.VerifyAll();

            // ensure all devices are in cache
            Assert.Equal(deviceCount, deviceList.Count(x => target.InternalGetCachedDevicesForDevAddr(x.DevAddr).Count == 1));

            target.ResetDeviceCache();
            Assert.False(deviceList.Any(x => target.InternalGetCachedDevicesForDevAddr(x.DevAddr).Count > 0), "Should not find devices again");
        }
Пример #3
0
        public async Task When_Fcnt_Down_Fails_Should_Stop_And_Not_Update_Device_Twin(uint initialFcntDown, uint initialFcntUp, uint payloadFcnt)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: null));

            simulatedDevice.FrmCntDown = initialFcntDown;
            simulatedDevice.FrmCntUp   = initialFcntUp;

            var devEUI  = simulatedDevice.LoRaDevice.DeviceID;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr;

            this.LoRaDeviceApi.Setup(x => x.ExecuteFunctionBundlerAsync(devEUI, It.IsAny <FunctionBundlerRequest>())).ReturnsAsync(() => new FunctionBundlerResult
            {
                AdrResult = new LoRaTools.ADR.LoRaADRResult {
                    CanConfirmToDevice = true, FCntDown = 0
                },
                NextFCntDown = 0
            });

            this.LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(devEUI, It.IsAny <uint>(), It.IsNotNull <string>()))
            .ReturnsAsync(true);

            var memoryCache = new MemoryCache(new MemoryCacheOptions());
            var device      = this.CreateLoRaDevice(simulatedDevice);

            var dicForDevAddr = new DevEUIToLoRaDeviceDictionary();

            dicForDevAddr.TryAdd(devEUI, device);
            memoryCache.Set(devAddr, dicForDevAddr);
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageDispatcher = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("hello", fcnt: payloadFcnt);
            var rxpk    = unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var request = this.CreateWaitableRequest(rxpk);

            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.Null(request.ResponseDownlink);

            // verify that the device in device registry has correct properties and frame counters
            var devicesForDevAddr = deviceRegistry.InternalGetCachedDevicesForDevAddr(devAddr);

            Assert.Single(devicesForDevAddr);
            Assert.True(devicesForDevAddr.TryGetValue(devEUI, out var loRaDevice));
            Assert.Equal(devAddr, loRaDevice.DevAddr);
            Assert.Equal(devEUI, loRaDevice.DevEUI);
            Assert.True(loRaDevice.IsABP);
            Assert.Equal(initialFcntUp, loRaDevice.FCntUp);
            Assert.Equal(initialFcntDown, loRaDevice.FCntDown);
            Assert.False(loRaDevice.HasFrameCountChanges);

            this.LoRaDeviceClient.VerifyAll();
            this.LoRaDeviceApi.VerifyAll();
        }
Пример #4
0
        public async Task When_Fcnt_Down_Fails_Should_Stop_And_Not_Update_Device_Twin(int initialFcntDown, int initialFcntUp, int payloadFcnt)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: null));

            simulatedDevice.FrmCntDown = initialFcntDown;
            simulatedDevice.FrmCntUp   = initialFcntUp;
            var loRaDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict);

            var devEUI  = simulatedDevice.LoRaDevice.DeviceID;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr;

            // Lora device api
            var loRaDeviceApi = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);

            loRaDeviceApi.Setup(x => x.NextFCntDownAsync(devEUI, initialFcntDown, payloadFcnt, ServerGatewayID)).ReturnsAsync((ushort)0);

            // using factory to create mock of
            var loRaDeviceFactory = new TestLoRaDeviceFactory(loRaDeviceClient.Object);

            var memoryCache = new MemoryCache(new MemoryCacheOptions());
            var device      = TestUtils.CreateFromSimulatedDevice(simulatedDevice, loRaDeviceClient.Object);

            var dicForDevAddr = new DevEUIToLoRaDeviceDictionary();

            dicForDevAddr.TryAdd(devEUI, device);
            memoryCache.Set(devAddr, dicForDevAddr);
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, loRaDeviceApi.Object, loRaDeviceFactory);

            var frameCounterUpdateStrategyFactory = new LoRaDeviceFrameCounterUpdateStrategyFactory(this.ServerConfiguration.GatewayID, loRaDeviceApi.Object);

            // Send to message processor
            var messageProcessor = new MessageProcessor(
                this.ServerConfiguration,
                deviceRegistry,
                frameCounterUpdateStrategyFactory,
                new LoRaPayloadDecoder());

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("hello", fcnt: payloadFcnt);
            var rxpk = unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var unconfirmedMessageResult = await messageProcessor.ProcessMessageAsync(rxpk);

            Assert.Null(unconfirmedMessageResult);

            // verify that the device in device registry has correct properties and frame counters
            var devicesForDevAddr = deviceRegistry.InternalGetCachedDevicesForDevAddr(devAddr);

            Assert.Single(devicesForDevAddr);
            Assert.True(devicesForDevAddr.TryGetValue(devEUI, out var loRaDevice));
            Assert.Equal(devAddr, loRaDevice.DevAddr);
            Assert.Equal(devEUI, loRaDevice.DevEUI);
            Assert.True(loRaDevice.IsABP);
            Assert.Equal(initialFcntUp, loRaDevice.FCntUp);
            Assert.Equal(initialFcntDown, loRaDevice.FCntDown);
            Assert.False(loRaDevice.HasFrameCountChanges);

            loRaDeviceClient.VerifyAll();
            loRaDeviceApi.VerifyAll();
        }
Пример #5
0
        public async Task When_Cache_Clear_Is_Called_Should_Removed_Cached_Devices(string deviceGatewayID)
        {
            const int deviceCount     = 10;
            var       foundDeviceList = new HashSet <LoRaDevice>();

            var apiService    = new Mock <LoRaDeviceAPIServiceBase>();
            var deviceFactory = new TestLoRaDeviceFactory(this.loRaDeviceClient.Object);
            var target        = new LoRaDeviceRegistry(this.serverConfiguration, this.cache, apiService.Object, deviceFactory);

            var getTwinMockSequence = this.loRaDeviceClient.SetupSequence(x => x.GetTwinAsync());

            for (var deviceID = 1; deviceID <= deviceCount; ++deviceID)
            {
                var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice((uint)deviceID, gatewayID: deviceGatewayID));
                var payload         = simulatedDevice.CreateUnconfirmedDataUpMessage("1234");
                payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey); // force mic creation

                var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DeviceID, string.Empty);
                apiService.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr))
                .ReturnsAsync(new SearchDevicesResult(iotHubDeviceInfo.AsList()));

                // device will be initialized
                getTwinMockSequence.ReturnsAsync(simulatedDevice.CreateABPTwin());

                var actual = await target.GetDeviceForPayloadAsync(payload);

                Assert.NotNull(actual);
                foundDeviceList.Add(actual);
            }

            Assert.Equal(deviceCount, foundDeviceList.Count);

            // Device was searched by DevAddr
            apiService.VerifyAll();

            // Device was created by factory
            this.loraDeviceFactoryMock.VerifyAll();

            // ensure all devices are in cache
            Assert.Equal(deviceCount, foundDeviceList.Count(x => target.InternalGetCachedDevicesForDevAddr(x.DevAddr).Count == 1));

            target.ResetDeviceCache();
            Assert.False(foundDeviceList.Any(x => target.InternalGetCachedDevicesForDevAddr(x.DevAddr).Count > 0), "Should not find devices again");
        }
Пример #6
0
        public async Task When_Device_Is_Assigned_To_Another_Gateway_After_Getting_Twin_Cache_Locally_And_Return_Null()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: "another-gateway"));

            var apiService       = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);
            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DeviceID, string.Empty);

            apiService.Setup(x => x.SearchByDevAddrAsync(It.IsNotNull <string>()))
            .ReturnsAsync(new SearchDevicesResult(iotHubDeviceInfo.AsList()));

            this.loRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(simulatedDevice.CreateABPTwin());
            this.loRaDeviceClient.Setup(x => x.Disconnect())
            .Returns(true);

            var deviceFactory = new TestLoRaDeviceFactory(this.loRaDeviceClient.Object);

            var target = new LoRaDeviceRegistry(this.serverConfiguration, this.cache, apiService.Object, deviceFactory);

            // request #1
            var payload1 = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 11);

            payload1.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey);
            var request1 = new WaitableLoRaRequest(payload1);

            target.GetLoRaRequestQueue(request1).Queue(request1);
            Assert.True(await request1.WaitCompleteAsync());
            Assert.True(request1.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request1.ProcessingFailedReason);

            // request #2
            var payload2 = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 12);

            payload2.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey);
            var request2 = new WaitableLoRaRequest(payload2);

            target.GetLoRaRequestQueue(request2).Queue(request2);
            Assert.True(await request2.WaitCompleteAsync());
            Assert.True(request2.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request2.ProcessingFailedReason);

            // Device was searched by DevAddr
            apiService.VerifyAll();
            apiService.Verify(x => x.SearchByDevAddrAsync(It.IsNotNull <string>()), Times.Once());

            this.loRaDeviceClient.VerifyAll();
            this.loRaDeviceClient.Verify(x => x.GetTwinAsync(), Times.Once());

            // device is in cache
            var devAddrDictionary = target.InternalGetCachedDevicesForDevAddr(LoRaTools.Utils.ConversionHelper.ByteArrayToString(payload1.DevAddr));

            Assert.NotNull(devAddrDictionary);
            Assert.True(devAddrDictionary.TryGetValue(simulatedDevice.DevEUI, out var cachedLoRaDevice));
            Assert.False(cachedLoRaDevice.IsOurDevice);
        }
Пример #7
0
        public async Task When_New_ABP_Device_Instance_Is_Created_Should_Increment_FCntDown()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerGatewayID));

            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DeviceID, string.Empty);

            this.LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(It.IsNotNull <string>()))
            .ReturnsAsync(new SearchDevicesResult(iotHubDeviceInfo.AsList()));

            // device will:
            // - be initialized
            // - send event
            // - receive c2d
            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(simulatedDevice.CreateABPTwin());
            this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);
            this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewMemoryCache(), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageProcessor = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 2);
            var rxpk    = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var request = this.CreateWaitableRequest(rxpk, constantElapsedTime: TimeSpan.FromMilliseconds(300));

            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingSucceeded);

            // Wait until loader updates the device cache
            await Task.Delay(50);

            var devices = deviceRegistry.InternalGetCachedDevicesForDevAddr(simulatedDevice.DevAddr);

            Assert.Single(devices);
            Assert.True(devices.TryGetValue(simulatedDevice.DevEUI, out var cachedDevice));
            Assert.True(cachedDevice.IsOurDevice);
            Assert.Equal(Constants.MAX_FCNT_UNSAVED_DELTA - 1U, cachedDevice.FCntDown);
            Assert.Equal(payload.GetFcnt(), (ushort)cachedDevice.FCntUp);

            // Device was searched by DevAddr
            this.LoRaDeviceApi.VerifyAll();

            // Device was created by factory
            this.LoRaDeviceClient.VerifyAll();
        }
Пример #8
0
        public async Task When_Device_Is_Not_In_Cache_And_Found_In_Api_Should_Cache_And_Process_Request(string deviceGatewayID)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID));
            var payload         = simulatedDevice.CreateUnconfirmedDataUpMessage("1234");

            payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey);

            var apiService       = new Mock <LoRaDeviceAPIServiceBase>();
            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DeviceID, string.Empty);

            apiService.Setup(x => x.SearchByDevAddrAsync(It.IsNotNull <string>()))
            .ReturnsAsync(new SearchDevicesResult(iotHubDeviceInfo.AsList()));

            // device will be initialized
            this.loRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(simulatedDevice.CreateABPTwin());

            var request        = new WaitableLoRaRequest(payload);
            var requestHandler = new Mock <ILoRaDataRequestHandler>(MockBehavior.Strict);

            requestHandler.Setup(x => x.ProcessRequestAsync(request, It.IsNotNull <LoRaDevice>()))
            .ReturnsAsync(new LoRaDeviceRequestProcessResult(null, request));

            var deviceFactory = new TestLoRaDeviceFactory(this.loRaDeviceClient.Object, requestHandler.Object);

            var target = new LoRaDeviceRegistry(this.serverConfiguration, this.cache, apiService.Object, deviceFactory);

            target.GetLoRaRequestQueue(request).Queue(request);

            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingSucceeded);

            // Device was searched by DevAddr
            apiService.VerifyAll();

            // ensure device is in cache
            var cachedItem = target.InternalGetCachedDevicesForDevAddr(simulatedDevice.DevAddr);

            Assert.NotNull(cachedItem);
            Assert.Single(cachedItem);
            Assert.True(cachedItem.TryGetValue(simulatedDevice.DevEUI, out var actualCachedLoRaDevice));

            // request was handled
            requestHandler.VerifyAll();
        }
Пример #9
0
        public async Task When_Device_Is_Assigned_To_Another_Gateway_Cache_Locally_And_Return_Null()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: "another-gateway"));
            var payload         = simulatedDevice.CreateUnconfirmedDataUpMessage("1234");

            var apiService       = new Mock <LoRaDeviceAPIServiceBase>();
            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DeviceID, string.Empty);

            apiService.Setup(x => x.SearchByDevAddrAsync(It.IsNotNull <string>()))
            .ReturnsAsync(new SearchDevicesResult(iotHubDeviceInfo.AsList()));

            var createdLoraDevice = TestUtils.CreateFromSimulatedDevice(simulatedDevice, this.loRaDeviceClient.Object);

            this.loraDeviceFactoryMock.Setup(x => x.Create(iotHubDeviceInfo))
            .Returns(createdLoraDevice);

            // device will be initialized
            this.loRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(simulatedDevice.CreateABPTwin());

            var target = new LoRaDeviceRegistry(this.serverConfiguration, this.cache, apiService.Object, this.loraDeviceFactoryMock.Object);

            var actual = await target.GetDeviceForPayloadAsync(payload);

            Assert.Null(actual);

            // search again
            var actual2 = await target.GetDeviceForPayloadAsync(payload);

            Assert.Null(actual2);

            // Device was searched by DevAddr
            apiService.VerifyAll();

            // Device was created by factory
            this.loraDeviceFactoryMock.VerifyAll();

            // device is in cache
            Assert.Equal(1, this.cache.Count);
            var devAddrDictionary = target.InternalGetCachedDevicesForDevAddr(LoRaTools.Utils.ConversionHelper.ByteArrayToString(payload.DevAddr));

            Assert.NotNull(devAddrDictionary);
            Assert.True(devAddrDictionary.TryGetValue(createdLoraDevice.DevEUI, out var cachedLoRaDevice));
            Assert.False(cachedLoRaDevice.IsOurDevice);
        }
Пример #10
0
        public async Task When_Device_Is_Not_In_Cache_And_Found_In_Api_Should_Cache_And_Return_It(string deviceGatewayID)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID));
            var payload         = simulatedDevice.CreateUnconfirmedDataUpMessage("1234");

            payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey);

            var apiService       = new Mock <LoRaDeviceAPIServiceBase>();
            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DeviceID, string.Empty);

            apiService.Setup(x => x.SearchByDevAddrAsync(It.IsNotNull <string>()))
            .ReturnsAsync(new SearchDevicesResult(iotHubDeviceInfo.AsList()));

            var createdLoraDevice = TestUtils.CreateFromSimulatedDevice(simulatedDevice, this.loRaDeviceClient.Object);

            this.loraDeviceFactoryMock.Setup(x => x.Create(iotHubDeviceInfo))
            .Returns(createdLoraDevice);

            // device will be initialized
            this.loRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(simulatedDevice.CreateABPTwin());

            var target = new LoRaDeviceRegistry(this.serverConfiguration, this.cache, apiService.Object, this.loraDeviceFactoryMock.Object);

            var actual = await target.GetDeviceForPayloadAsync(payload);

            Assert.NotNull(actual);
            Assert.Same(actual, createdLoraDevice);
            Assert.True(actual.IsOurDevice);

            // Device was searched by DevAddr
            apiService.VerifyAll();

            // Device was created by factory
            this.loraDeviceFactoryMock.VerifyAll();

            // ensure device is in cache
            var cachedItem = target.InternalGetCachedDevicesForDevAddr(createdLoraDevice.DevAddr);

            Assert.NotNull(cachedItem);
            Assert.Single(cachedItem);
            Assert.True(cachedItem.TryGetValue(createdLoraDevice.DevEUI, out var actualCachedLoRaDevice));
            Assert.Same(createdLoraDevice, actualCachedLoRaDevice);
        }
Пример #11
0
        public async Task ABP_New_Loaded_Device_With_Fcnt_1_Or_0_Should_Reset_Fcnt_And_Send_To_IotHub(
            string twinGatewayID,
            uint payloadFcntUp,
            uint?deviceTwinFcntUp,
            uint?deviceTwinFcntDown)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: null));

            var devEUI  = simulatedDevice.LoRaDevice.DeviceID;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr;

            // message will be sent
            LoRaDeviceTelemetry loRaDeviceTelemetry = null;

            this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => loRaDeviceTelemetry = t)
            .ReturnsAsync(true);

            // C2D message will be checked
            this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            // twin will be loaded
            var initialTwin = new Twin();

            initialTwin.Properties.Desired[TwinProperty.DevEUI]  = devEUI;
            initialTwin.Properties.Desired[TwinProperty.AppEUI]  = simulatedDevice.LoRaDevice.AppEUI;
            initialTwin.Properties.Desired[TwinProperty.AppKey]  = simulatedDevice.LoRaDevice.AppKey;
            initialTwin.Properties.Desired[TwinProperty.NwkSKey] = simulatedDevice.LoRaDevice.NwkSKey;
            initialTwin.Properties.Desired[TwinProperty.AppSKey] = simulatedDevice.LoRaDevice.AppSKey;
            initialTwin.Properties.Desired[TwinProperty.DevAddr] = devAddr;
            if (twinGatewayID != null)
            {
                initialTwin.Properties.Desired[TwinProperty.GatewayID] = twinGatewayID;
            }
            initialTwin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            if (deviceTwinFcntDown.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntDown] = deviceTwinFcntDown.Value;
            }
            if (deviceTwinFcntUp.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntUp] = deviceTwinFcntUp.Value;
            }

            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync()).ReturnsAsync(initialTwin);

            // twin will be updated with new fcnt
            int?fcntUpSavedInTwin   = null;
            int?fcntDownSavedInTwin = null;

            var shouldSaveTwin = (deviceTwinFcntDown ?? 0) != 0 || (deviceTwinFcntUp ?? 0) != 0;

            if (shouldSaveTwin)
            {
                this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
                .Callback <TwinCollection>((t) =>
                {
                    fcntUpSavedInTwin   = (int)t[TwinProperty.FCntUp];
                    fcntDownSavedInTwin = (int)t[TwinProperty.FCntDown];
                })
                .ReturnsAsync(true);
            }

            this.LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(devEUI, It.IsAny <uint>(), It.IsNotNull <string>()))
            .ReturnsAsync(true);

            // device api will be searched for payload
            this.LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(devAddr))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "abc").AsList()));

            var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageDispatcher = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("hello", fcnt: payloadFcntUp);
            var rxpk    = unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var request = this.CreateWaitableRequest(rxpk);

            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.Null(request.ResponseDownlink);

            // Ensure that a telemetry was sent
            Assert.NotNull(loRaDeviceTelemetry);

            // Ensure that the device twins were saved
            if (shouldSaveTwin)
            {
                Assert.NotNull(fcntDownSavedInTwin);
                Assert.NotNull(fcntUpSavedInTwin);
                Assert.Equal(0, fcntDownSavedInTwin.Value);
                Assert.Equal(0, fcntUpSavedInTwin.Value);
            }

            // verify that the device in device registry has correct properties and frame counters
            var devicesForDevAddr = deviceRegistry.InternalGetCachedDevicesForDevAddr(devAddr);

            Assert.Single(devicesForDevAddr);
            Assert.True(devicesForDevAddr.TryGetValue(devEUI, out var loRaDevice));
            Assert.Equal(devAddr, loRaDevice.DevAddr);
            Assert.Equal(devEUI, loRaDevice.DevEUI);
            Assert.True(loRaDevice.IsABP);
            Assert.Equal(payloadFcntUp, loRaDevice.FCntUp);
            Assert.Equal(0U, loRaDevice.FCntDown);
            if (payloadFcntUp == 0)
            {
                Assert.False(loRaDevice.HasFrameCountChanges); // no changes
            }
            else
            {
                Assert.True(loRaDevice.HasFrameCountChanges); // should have changes!
            }
            this.LoRaDeviceClient.VerifyAll();
            this.LoRaDeviceApi.VerifyAll();
        }
Пример #12
0
        public async Task When_Getting_C2D_Message_Fails_To_Resolve_Fcnt_Down_Should_Drop_Message_And_Return_Null()
        {
            const uint initialFcntDown = 5;
            const uint initialFcntUp   = 21;
            const uint payloadFcnt     = 23;

            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: null));

            simulatedDevice.FrmCntUp   = initialFcntUp;
            simulatedDevice.FrmCntDown = initialFcntDown;

            var devEUI  = simulatedDevice.LoRaDevice.DeviceID;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr;

            // message will be sent
            this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

            var c2dMessage = new ReceivedLoRaCloudToDeviceMessage()
            {
                Fport = 1
            };
            var cloudToDeviceMessage = c2dMessage.CreateMessage();

            // C2D message will be retrieved
            this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>()))
            .ReturnsAsync(cloudToDeviceMessage);

            // C2D message will be abandonned
            this.LoRaDeviceClient.Setup(x => x.AbandonAsync(cloudToDeviceMessage))
            .ReturnsAsync(true);

            // getting the fcnt down will return 0!
            this.LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(devEUI, initialFcntDown, payloadFcnt, this.ServerConfiguration.GatewayID))
            .ReturnsAsync((ushort)0);

            var cachedDevice   = this.CreateLoRaDevice(simulatedDevice);
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(cachedDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageDispatcher = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("hello", fcnt: payloadFcnt);
            var rxpk = unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var unconfirmedRequest = this.CreateWaitableRequest(rxpk);

            messageDispatcher.DispatchRequest(unconfirmedRequest);
            Assert.True(await unconfirmedRequest.WaitCompleteAsync());
            Assert.Null(unconfirmedRequest.ResponseDownlink);

            var cachedDevices = deviceRegistry.InternalGetCachedDevicesForDevAddr(simulatedDevice.DevAddr);

            Assert.True(cachedDevices.TryGetValue(devEUI, out var loRaDevice));
            // fcnt down did not change
            Assert.Equal(initialFcntDown, loRaDevice.FCntDown);

            // fcnt up changed
            Assert.Equal(unconfirmedMessagePayload.GetFcnt(), loRaDevice.FCntUp);

            this.LoRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsAny <TimeSpan>()), Times.Once());
            this.LoRaDeviceClient.Verify(x => x.AbandonAsync(It.IsAny <Message>()), Times.Once());

            this.LoRaDeviceClient.VerifyAll();
            this.LoRaDeviceApi.VerifyAll();
        }
Пример #13
0
        public async Task Join_And_Send_Unconfirmed_And_Confirmed_Messages(string deviceGatewayID, int initialFcntUp, int initialFcntDown, int startingPayloadFcnt, uint netId)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequest     = simulatedDevice.CreateJoinRequest();

            // Create Rxpk
            var joinRxpk = joinRequest.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];

            var devNonce = LoRaTools.Utils.ConversionHelper.ByteArrayToString(joinRequest.DevNonce);
            var devAddr  = string.Empty;
            var devEUI   = simulatedDevice.LoRaDevice.DeviceID;
            var appEUI   = simulatedDevice.LoRaDevice.AppEUI;

            var loRaDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict);

            this.ServerConfiguration.NetId = netId;
            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEUI;
            twin.Properties.Desired[TwinProperty.AppEUI]        = simulatedDevice.LoRaDevice.AppEUI;
            twin.Properties.Desired[TwinProperty.AppKey]        = simulatedDevice.LoRaDevice.AppKey;
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            twin.Properties.Reported[TwinProperty.FCntUp]       = initialFcntUp;
            twin.Properties.Reported[TwinProperty.FCntDown]     = initialFcntDown;
            loRaDeviceClient.Setup(x => x.GetTwinAsync()).ReturnsAsync(twin);

            // Device twin will be updated
            string afterJoinAppSKey  = null;
            string afterJoinNwkSKey  = null;
            string afterJoinDevAddr  = null;
            int    afterJoinFcntDown = -1;
            int    afterJoinFcntUp   = -1;

            loRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .Callback <TwinCollection>((updatedTwin) =>
            {
                afterJoinAppSKey  = updatedTwin[TwinProperty.AppSKey];
                afterJoinNwkSKey  = updatedTwin[TwinProperty.NwkSKey];
                afterJoinDevAddr  = updatedTwin[TwinProperty.DevAddr];
                afterJoinFcntDown = updatedTwin[TwinProperty.FCntDown];
                afterJoinFcntUp   = updatedTwin[TwinProperty.FCntUp];
            })
            .ReturnsAsync(true);

            // message will be sent
            var sentTelemetry = new List <LoRaDeviceTelemetry>();

            loRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => sentTelemetry.Add(t))
            .ReturnsAsync(true);

            // C2D message will be checked
            loRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            // Lora device api will be search by devices with matching deveui,
            var loRaDeviceApi = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);

            loRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, devNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            // multi gateway will request a next frame count down from the lora device api, prepare it
            if (string.IsNullOrEmpty(deviceGatewayID))
            {
                loRaDeviceApi.Setup(x => x.NextFCntDownAsync(devEUI, 0, startingPayloadFcnt + 1, this.ServerConfiguration.GatewayID))
                .ReturnsAsync((ushort)1);
            }

            // using factory to create mock of
            var loRaDeviceFactory = new TestLoRaDeviceFactory(loRaDeviceClient.Object);

            var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, loRaDeviceApi.Object, loRaDeviceFactory);

            var frameCounterUpdateStrategyFactory = new LoRaDeviceFrameCounterUpdateStrategyFactory(this.ServerConfiguration.GatewayID, loRaDeviceApi.Object);

            // Send to message processor
            var messageProcessor = new MessageProcessor(
                this.ServerConfiguration,
                deviceRegistry,
                frameCounterUpdateStrategyFactory,
                new LoRaPayloadDecoder());

            var downlinkJoinAcceptMessage = await messageProcessor.ProcessMessageAsync(joinRxpk);

            Assert.NotNull(downlinkJoinAcceptMessage);
            var joinAccept = new LoRaPayloadJoinAccept(Convert.FromBase64String(downlinkJoinAcceptMessage.Txpk.Data), simulatedDevice.LoRaDevice.AppKey);

            Assert.Equal(joinAccept.DevAddr.ToArray(), ConversionHelper.StringToByteArray(afterJoinDevAddr));

            // check that the device is in cache
            var devicesForDevAddr = deviceRegistry.InternalGetCachedDevicesForDevAddr(afterJoinDevAddr);

            Assert.Single(devicesForDevAddr); // should have the single device
            Assert.True(devicesForDevAddr.TryGetValue(devEUI, out var loRaDevice));
            Assert.Equal(afterJoinAppSKey, loRaDevice.AppSKey);
            Assert.Equal(afterJoinNwkSKey, loRaDevice.NwkSKey);
            Assert.Equal(afterJoinDevAddr, loRaDevice.DevAddr);
            var netIdBytes = BitConverter.GetBytes(netId);

            Assert.Equal((uint)(netIdBytes[0] & 0b01111111), NetIdHelper.GetNwkIdPart(afterJoinDevAddr));
            if (deviceGatewayID == null)
            {
                Assert.Null(loRaDevice.GatewayID);
            }
            else
            {
                Assert.Equal(deviceGatewayID, loRaDevice.GatewayID);
            }

            // fcnt is restarted
            Assert.Equal(0, afterJoinFcntDown);
            Assert.Equal(0, afterJoinFcntUp);
            Assert.Equal(0, loRaDevice.FCntUp);
            Assert.Equal(0, loRaDevice.FCntDown);
            Assert.False(loRaDevice.HasFrameCountChanges);

            simulatedDevice.LoRaDevice.AppSKey = afterJoinAppSKey;
            simulatedDevice.LoRaDevice.NwkSKey = afterJoinNwkSKey;
            simulatedDevice.LoRaDevice.DevAddr = afterJoinDevAddr;

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("100", fcnt: startingPayloadFcnt);
            var unconfirmedMessageResult  = await messageProcessor.ProcessMessageAsync(unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]);

            Assert.Null(unconfirmedMessageResult);

            // fcnt up was updated
            Assert.Equal(startingPayloadFcnt, loRaDevice.FCntUp);
            Assert.Equal(0, loRaDevice.FCntDown);

            if (startingPayloadFcnt != 0)
            {
                // Frame change flag will be set, only saving every 10 messages
                Assert.True(loRaDevice.HasFrameCountChanges);
            }

            Assert.Single(sentTelemetry);

            // sends confirmed message
            var confirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("200", fcnt: startingPayloadFcnt + 1);
            var confirmedMessageRxpk    = confirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var confirmedMessage        = await messageProcessor.ProcessMessageAsync(confirmedMessageRxpk);

            Assert.NotNull(confirmedMessage);
            Assert.NotNull(confirmedMessage.Txpk);
            Assert.Equal(2, sentTelemetry.Count);

            // validates txpk according to eu region
            Assert.Equal(RegionFactory.CreateEU868Region().GetDownstreamChannelFrequency(confirmedMessageRxpk), confirmedMessage.Txpk.Freq);
            Assert.Equal("4/5", confirmedMessage.Txpk.Codr);
            Assert.False(confirmedMessage.Txpk.Imme);
            Assert.True(confirmedMessage.Txpk.Ipol);
            Assert.Equal("LORA", confirmedMessage.Txpk.Modu);

            // fcnt up was updated
            Assert.Equal(startingPayloadFcnt + 1, loRaDevice.FCntUp);
            Assert.Equal(1, loRaDevice.FCntDown);

            // Frame change flag will be set, only saving every 10 messages
            Assert.True(loRaDevice.HasFrameCountChanges);

            // C2D message will be checked twice
            loRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()), Times.Exactly(2));

            // has telemetry with both fcnt
            Assert.Single(sentTelemetry, (t) => t.Fcnt == startingPayloadFcnt);
            Assert.Single(sentTelemetry, (t) => t.Fcnt == (startingPayloadFcnt + 1));

            loRaDeviceClient.VerifyAll();
            loRaDeviceApi.VerifyAll();
        }
Пример #14
0
        public async Task ABP_From_Another_Gateway_Unconfirmed_Message_Should_Load_Device_Cache_And_Disconnect()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: "another-gateway"));

            simulatedDevice.FrmCntUp = 9;

            this.LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simulatedDevice.DevAddr, simulatedDevice.DevEUI, "1234").AsList()));

            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(TestUtils.CreateABPTwin(simulatedDevice));

            this.LoRaDeviceClient.Setup(x => x.Disconnect())
            .Returns(true);

            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewMemoryCache(), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageProcessor = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            var payload1 = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 10);
            var rxpk1    = payload1.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var request1 = this.CreateWaitableRequest(rxpk1);

            messageProcessor.DispatchRequest(request1);
            Assert.True(await request1.WaitCompleteAsync());
            Assert.True(request1.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request1.ProcessingFailedReason);

            // Let loading finish
            await Task.Delay(50);

            // device should be cached
            var cachedDevices = deviceRegistry.InternalGetCachedDevicesForDevAddr(simulatedDevice.DevAddr);

            Assert.Single(cachedDevices);
            Assert.True(cachedDevices.TryGetValue(simulatedDevice.DevEUI, out var cachedDevice));
            Assert.Equal(9U, cachedDevice.FCntUp);

            var payload2 = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 11);
            var rxpk2    = payload2.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var request2 = this.CreateWaitableRequest(rxpk2);

            messageProcessor.DispatchRequest(request2);
            Assert.True(await request2.WaitCompleteAsync());
            Assert.True(request2.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request2.ProcessingFailedReason);

            // Expectations
            // 1. Message was sent to IoT Hub
            this.LoRaDeviceClient.VerifyAll();
            this.LoRaDeviceApi.VerifyAll();
            this.LoRaDeviceApi.Verify(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr), Times.Once());

            // 2. Frame counter up was not updated
            cachedDevices = deviceRegistry.InternalGetCachedDevicesForDevAddr(simulatedDevice.DevAddr);
            Assert.Single(cachedDevices);
            cachedDevice = null;
            Assert.True(cachedDevices.TryGetValue(simulatedDevice.DevEUI, out cachedDevice));
            Assert.Equal(9U, cachedDevice.FCntUp);
        }
        public async Task ABP_Load_And_Receiving_Multiple_Unconfirmed_Should_Send_All_ToHub(ParallelTestConfiguration parallelTestConfiguration)
        {
            Console.WriteLine("---");
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(parallelTestConfiguration.DeviceID ?? 1, gatewayID: null));

            var devEUI  = simulatedDevice.LoRaDevice.DeviceID;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr;

            // Using loose mock because sometimes we might call receive async
            var looseDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Loose);

            this.LoRaDeviceFactory.SetClient(devEUI, looseDeviceClient.Object);

            looseDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>()))
            .ReturnsAsync((Message)null);

            // message will be sent
            var sentTelemetry = new List <LoRaDeviceTelemetry>();

            looseDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Returns <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) =>
            {
                sentTelemetry.Add(t);
                var duration = parallelTestConfiguration.SendEventDuration.Next();
                Console.WriteLine($"{nameof(looseDeviceClient.Object.SendEventAsync)} sleeping for {duration}");
                return(Task.Delay(duration)
                       .ContinueWith((a) => true, TaskContinuationOptions.ExecuteSynchronously));
            });

            // twin will be loaded
            var initialTwin = new Twin();

            initialTwin.Properties.Desired[TwinProperty.DevEUI]  = devEUI;
            initialTwin.Properties.Desired[TwinProperty.AppEUI]  = simulatedDevice.LoRaDevice.AppEUI;
            initialTwin.Properties.Desired[TwinProperty.AppKey]  = simulatedDevice.LoRaDevice.AppKey;
            initialTwin.Properties.Desired[TwinProperty.NwkSKey] = simulatedDevice.LoRaDevice.NwkSKey;
            initialTwin.Properties.Desired[TwinProperty.AppSKey] = simulatedDevice.LoRaDevice.AppSKey;
            initialTwin.Properties.Desired[TwinProperty.DevAddr] = devAddr;
            if (parallelTestConfiguration.GatewayID != null)
            {
                initialTwin.Properties.Desired[TwinProperty.GatewayID] = parallelTestConfiguration.GatewayID;
            }
            initialTwin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            if (parallelTestConfiguration.DeviceTwinFcntDown.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntDown] = parallelTestConfiguration.DeviceTwinFcntDown.Value;
            }
            if (parallelTestConfiguration.DeviceTwinFcntUp.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntUp] = parallelTestConfiguration.DeviceTwinFcntUp.Value;
            }

            looseDeviceClient.Setup(x => x.GetTwinAsync())
            .Returns(() =>
            {
                var duration = parallelTestConfiguration.LoadTwinDuration.Next();
                Console.WriteLine($"{nameof(looseDeviceClient.Object.GetTwinAsync)} sleeping for {duration}");
                return(Task.Delay(duration)
                       .ContinueWith(_ => initialTwin, TaskContinuationOptions.ExecuteSynchronously));
            });

            // twin will be updated with new fcnt
            var  expectedToSaveTwin  = parallelTestConfiguration.DeviceTwinFcntDown > 0 || parallelTestConfiguration.DeviceTwinFcntUp > 0;
            uint?fcntUpSavedInTwin   = null;
            uint?fcntDownSavedInTwin = null;

            if (expectedToSaveTwin)
            {
                looseDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
                .Returns <TwinCollection>((t) =>
                {
                    fcntUpSavedInTwin   = (uint)t[TwinProperty.FCntUp];
                    fcntDownSavedInTwin = (uint)t[TwinProperty.FCntDown];
                    var duration        = parallelTestConfiguration.UpdateTwinDuration.Next();
                    Console.WriteLine($"{nameof(looseDeviceClient.Object.UpdateReportedPropertiesAsync)} sleeping for {duration}");
                    return(Task.Delay(duration)
                           .ContinueWith((a) => true, TaskContinuationOptions.ExecuteSynchronously));
                });
            }

            if (expectedToSaveTwin && string.IsNullOrEmpty(parallelTestConfiguration.GatewayID))
            {
                this.LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(devEUI))
                .Returns(() =>
                {
                    var duration = parallelTestConfiguration.DeviceApiResetFcntDuration.Next();
                    Console.WriteLine($"{nameof(this.LoRaDeviceApi.Object.ABPFcntCacheResetAsync)} sleeping for {duration}");
                    return(Task.Delay(duration)
                           .ContinueWith((a) => true, TaskContinuationOptions.ExecuteSynchronously));
                });
            }

            // device api will be searched for payload
            this.LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(devAddr))
            .Returns(() =>
            {
                var duration = parallelTestConfiguration.SearchByDevAddrDuration.Next();
                Console.WriteLine($"{nameof(this.LoRaDeviceApi.Object.SearchByDevAddrAsync)} sleeping for {duration}");
                return(Task.Delay(duration)
                       .ContinueWith((a) => new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "abc").AsList()), TaskContinuationOptions.ExecuteSynchronously));
            });

            var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageDispatcher = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            // sends unconfirmed message
            var unconfirmedMessage1 = simulatedDevice.CreateUnconfirmedMessageUplink("1", fcnt: 1).Rxpk[0];
            var unconfirmedMessage2 = simulatedDevice.CreateUnconfirmedMessageUplink("2", fcnt: 2).Rxpk[0];
            var unconfirmedMessage3 = simulatedDevice.CreateUnconfirmedMessageUplink("3", fcnt: 3).Rxpk[0];

            var req1 = new WaitableLoRaRequest(unconfirmedMessage1, this.packetForwarder);

            messageDispatcher.DispatchRequest(req1);
            await Task.Delay(parallelTestConfiguration.BetweenMessageDuration.Next());

            var req2 = new WaitableLoRaRequest(unconfirmedMessage2, this.packetForwarder);

            messageDispatcher.DispatchRequest(req2);
            await Task.Delay(parallelTestConfiguration.BetweenMessageDuration.Next());

            var req3 = new WaitableLoRaRequest(unconfirmedMessage3, this.packetForwarder);

            messageDispatcher.DispatchRequest(req3);
            await Task.Delay(parallelTestConfiguration.BetweenMessageDuration.Next());

            await Task.WhenAll(req1.WaitCompleteAsync(20000), req2.WaitCompleteAsync(20000), req3.WaitCompleteAsync(20000));

            var allRequests = new[] { req1, req2, req3 };

            Assert.All(allRequests, x => Assert.Null(x.ResponseDownlink));
            Assert.All(allRequests, x => Assert.True(x.ProcessingSucceeded));

            looseDeviceClient.Verify(x => x.GetTwinAsync(), Times.Exactly(1));
            if (expectedToSaveTwin)
            {
                looseDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>()), Times.Exactly(1));
            }

            this.LoRaDeviceApi.Verify(x => x.SearchByDevAddrAsync(devAddr), Times.Once);

            // Ensure that all telemetry was sent
            Assert.Equal(3, sentTelemetry.Count);

            // Ensure data was sent in order
            Assert.Equal(1, sentTelemetry[0].Fcnt);
            Assert.Equal(2, sentTelemetry[1].Fcnt);
            Assert.Equal(3, sentTelemetry[2].Fcnt);

            // Ensure that the device twins were saved
            if (expectedToSaveTwin)
            {
                Assert.NotNull(fcntDownSavedInTwin);
                Assert.NotNull(fcntUpSavedInTwin);
                Assert.Equal(0U, fcntDownSavedInTwin.Value);
                Assert.Equal(0U, fcntUpSavedInTwin.Value);
            }

            // verify that the device in device registry has correct properties and frame counters
            var devicesForDevAddr = deviceRegistry.InternalGetCachedDevicesForDevAddr(devAddr);

            Assert.Single(devicesForDevAddr);
            Assert.True(devicesForDevAddr.TryGetValue(devEUI, out var loRaDevice));
            Assert.Equal(devAddr, loRaDevice.DevAddr);
            Assert.Equal(devEUI, loRaDevice.DevEUI);
            Assert.True(loRaDevice.IsABP);
            Assert.Equal(3U, loRaDevice.FCntUp);
            Assert.Equal(0U, loRaDevice.FCntDown);
            Assert.True(loRaDevice.HasFrameCountChanges); // should have changes!

            // looseDeviceClient.VerifyAll();
            this.LoRaDeviceApi.VerifyAll();
        }
Пример #16
0
        public async Task When_Join_Fails_Due_To_GetTwin_Error_Second_Try_Should_Reload_Device_Twin(string deviceGatewayID)
        {
            var simulatedDevice     = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequestPayload1 = simulatedDevice.CreateJoinRequest();

            // Create Rxpk
            var joinRequestRxpk1 = joinRequestPayload1.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];

            var joinRequestDevNonce1 = ConversionHelper.ByteArrayToString(joinRequestPayload1.DevNonce);
            var devAddr = string.Empty;
            var devEUI  = simulatedDevice.LoRaDevice.DeviceID;
            var appEUI  = simulatedDevice.LoRaDevice.AppEUI;

            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEUI;
            twin.Properties.Desired[TwinProperty.AppEUI]        = simulatedDevice.LoRaDevice.AppEUI;
            twin.Properties.Desired[TwinProperty.AppKey]        = simulatedDevice.LoRaDevice.AppKey;
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            this.LoRaDeviceClient.SetupSequence(x => x.GetTwinAsync())
            .ReturnsAsync((Twin)null)
            .ReturnsAsync(twin);

            // Device twin will be updated
            string afterJoinAppSKey = null;
            string afterJoinNwkSKey = null;
            string afterJoinDevAddr = null;

            this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .Callback <TwinCollection>((updatedTwin) =>
            {
                afterJoinAppSKey = updatedTwin[TwinProperty.AppSKey];
                afterJoinNwkSKey = updatedTwin[TwinProperty.NwkSKey];
                afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr];
            })
            .ReturnsAsync(true);

            // Lora device api will be search by devices with matching deveui,
            this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, joinRequestDevNonce1))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            // using factory to create mock of
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewMemoryCache(), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            var messageProcessor = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            // 1st join request
            // Should fail
            var joinRequest1 = this.CreateWaitableRequest(joinRequestRxpk1);

            messageProcessor.DispatchRequest(joinRequest1);
            Assert.True(await joinRequest1.WaitCompleteAsync());
            Assert.True(joinRequest1.ProcessingFailed);
            Assert.Null(joinRequest1.ResponseDownlink);

            // 2nd attempt
            var joinRequestPayload2 = simulatedDevice.CreateJoinRequest();

            // will reload the device matched by deveui
            this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, ConversionHelper.ByteArrayToString(joinRequestPayload2.DevNonce)))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            var joinRequestRxpk2     = joinRequestPayload2.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];
            var joinRequestDevNonce2 = LoRaTools.Utils.ConversionHelper.ByteArrayToString(joinRequestPayload2.DevNonce);
            var joinRequest2         = this.CreateWaitableRequest(joinRequestRxpk2);

            messageProcessor.DispatchRequest(joinRequest2);
            Assert.True(await joinRequest2.WaitCompleteAsync());
            Assert.NotNull(joinRequest2.ResponseDownlink);
            Assert.True(joinRequest2.ProcessingSucceeded);
            Assert.Single(this.PacketForwarder.DownlinkMessages);
            var joinRequestDownlinkMessage2 = this.PacketForwarder.DownlinkMessages[0];
            var joinAccept = new LoRaPayloadJoinAccept(Convert.FromBase64String(joinRequestDownlinkMessage2.Txpk.Data), simulatedDevice.LoRaDevice.AppKey);

            Assert.Equal(joinAccept.DevAddr.ToArray(), ConversionHelper.StringToByteArray(afterJoinDevAddr));

            var devicesForDevAddr = deviceRegistry.InternalGetCachedDevicesForDevAddr(afterJoinDevAddr);

            Assert.Single(devicesForDevAddr); // should have the single device
            Assert.True(devicesForDevAddr.TryGetValue(devEUI, out var loRaDevice));

            Assert.Equal(simulatedDevice.AppKey, loRaDevice.AppKey);
            Assert.Equal(simulatedDevice.AppEUI, loRaDevice.AppEUI);
            Assert.Equal(afterJoinAppSKey, loRaDevice.AppSKey);
            Assert.Equal(afterJoinNwkSKey, loRaDevice.NwkSKey);
            Assert.Equal(afterJoinDevAddr, loRaDevice.DevAddr);
            if (deviceGatewayID == null)
            {
                Assert.Null(loRaDevice.GatewayID);
            }
            else
            {
                Assert.Equal(deviceGatewayID, loRaDevice.GatewayID);
            }

            // fcnt is restarted
            Assert.Equal(0U, loRaDevice.FCntUp);
            Assert.Equal(0U, loRaDevice.FCntDown);
            Assert.False(loRaDevice.HasFrameCountChanges);

            // should get twin 2x (1st failed)
            this.LoRaDeviceClient.Verify(x => x.GetTwinAsync(), Times.Exactly(2));

            // should get device for join 2x
            this.LoRaDeviceApi.Verify(x => x.SearchAndLockForJoinAsync(ServerGatewayID, devEUI, appEUI, It.IsAny <string>()));

            this.LoRaDeviceClient.VerifyAll();
            this.LoRaDeviceApi.VerifyAll();
        }
Пример #17
0
        public async Task Join_And_Send_Unconfirmed_And_Confirmed_Messages(string deviceGatewayID, uint initialFcntUp, uint initialFcntDown, uint startingPayloadFcnt, uint netId)
        {
            var simulatedDevice    = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequestPayload = simulatedDevice.CreateJoinRequest();

            // Create Rxpk
            var joinRxpk = joinRequestPayload.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];

            var devNonce = LoRaTools.Utils.ConversionHelper.ByteArrayToString(joinRequestPayload.DevNonce);
            var devAddr  = string.Empty;
            var devEUI   = simulatedDevice.LoRaDevice.DeviceID;
            var appEUI   = simulatedDevice.LoRaDevice.AppEUI;

            this.ServerConfiguration.NetId = netId;
            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEUI;
            twin.Properties.Desired[TwinProperty.AppEUI]        = simulatedDevice.LoRaDevice.AppEUI;
            twin.Properties.Desired[TwinProperty.AppKey]        = simulatedDevice.LoRaDevice.AppKey;
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            twin.Properties.Reported[TwinProperty.FCntUp]       = initialFcntUp;
            twin.Properties.Reported[TwinProperty.FCntDown]     = initialFcntDown;
            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync()).ReturnsAsync(twin);

            // Device twin will be updated
            string afterJoinAppSKey  = null;
            string afterJoinNwkSKey  = null;
            string afterJoinDevAddr  = null;
            uint   afterJoinFcntDown = 0;
            uint   afterJoinFcntUp   = 0;

            this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .Callback <TwinCollection>((updatedTwin) =>
            {
                afterJoinAppSKey  = updatedTwin[TwinProperty.AppSKey];
                afterJoinNwkSKey  = updatedTwin[TwinProperty.NwkSKey];
                afterJoinDevAddr  = updatedTwin[TwinProperty.DevAddr];
                afterJoinFcntDown = updatedTwin[TwinProperty.FCntDown];
                afterJoinFcntUp   = updatedTwin[TwinProperty.FCntUp];

                // should not save class C device properties
                Assert.False(updatedTwin.Contains(TwinProperty.Region));
                Assert.False(updatedTwin.Contains(TwinProperty.PreferredGatewayID));
            })
            .ReturnsAsync(true);

            // message will be sent
            var sentTelemetry = new List <LoRaDeviceTelemetry>();

            this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => sentTelemetry.Add(t))
            .ReturnsAsync(true);

            // C2D message will be checked
            this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            // Lora device api will be search by devices with matching deveui,
            this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, devNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            // multi gateway will request a next frame count down from the lora device api, prepare it
            if (string.IsNullOrEmpty(deviceGatewayID))
            {
                this.LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(devEUI, 0, startingPayloadFcnt + 1, this.ServerConfiguration.GatewayID))
                .ReturnsAsync((ushort)1);
                this.LoRaDeviceApi
                .Setup(x => x.ExecuteFunctionBundlerAsync(devEUI, It.IsAny <FunctionBundlerRequest>()))
                .ReturnsAsync(() => new FunctionBundlerResult
                {
                    AdrResult = new LoRaTools.ADR.LoRaADRResult
                    {
                        CanConfirmToDevice = false,
                        FCntDown           = 1,
                        NbRepetition       = 1,
                        TxPower            = 0
                    },
                    NextFCntDown = 1
                });
            }

            // using factory to create mock of
            var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageProcessor = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            var joinRequest = this.CreateWaitableRequest(joinRxpk, constantElapsedTime: TimeSpan.FromMilliseconds(300));

            messageProcessor.DispatchRequest(joinRequest);
            Assert.True(await joinRequest.WaitCompleteAsync());
            Assert.True(joinRequest.ProcessingSucceeded);
            Assert.NotNull(joinRequest.ResponseDownlink);
            Assert.Single(this.PacketForwarder.DownlinkMessages);
            var downlinkJoinAcceptMessage = this.PacketForwarder.DownlinkMessages[0];
            var joinAccept = new LoRaPayloadJoinAccept(Convert.FromBase64String(downlinkJoinAcceptMessage.Txpk.Data), simulatedDevice.LoRaDevice.AppKey);

            Assert.Equal(joinAccept.DevAddr.ToArray(), ConversionHelper.StringToByteArray(afterJoinDevAddr));

            // check that the device is in cache
            var devicesForDevAddr = deviceRegistry.InternalGetCachedDevicesForDevAddr(afterJoinDevAddr);

            Assert.Single(devicesForDevAddr); // should have the single device
            Assert.True(devicesForDevAddr.TryGetValue(devEUI, out var loRaDevice));
            Assert.Equal(afterJoinAppSKey, loRaDevice.AppSKey);
            Assert.Equal(afterJoinNwkSKey, loRaDevice.NwkSKey);
            Assert.Equal(afterJoinDevAddr, loRaDevice.DevAddr);
            var netIdBytes = BitConverter.GetBytes(netId);

            Assert.Equal((uint)(netIdBytes[0] & 0b01111111), NetIdHelper.GetNwkIdPart(afterJoinDevAddr));
            if (deviceGatewayID == null)
            {
                Assert.Null(loRaDevice.GatewayID);
            }
            else
            {
                Assert.Equal(deviceGatewayID, loRaDevice.GatewayID);
            }

            // fcnt is restarted
            Assert.Equal(0U, afterJoinFcntDown);
            Assert.Equal(0U, afterJoinFcntUp);
            Assert.Equal(0U, loRaDevice.FCntUp);
            Assert.Equal(0U, loRaDevice.FCntDown);
            Assert.False(loRaDevice.HasFrameCountChanges);

            simulatedDevice.LoRaDevice.AppSKey = afterJoinAppSKey;
            simulatedDevice.LoRaDevice.NwkSKey = afterJoinNwkSKey;
            simulatedDevice.LoRaDevice.DevAddr = afterJoinDevAddr;

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("100", fcnt: startingPayloadFcnt);
            var unconfirmedRequest        = this.CreateWaitableRequest(unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0], constantElapsedTime: TimeSpan.FromMilliseconds(300));

            messageProcessor.DispatchRequest(unconfirmedRequest);
            Assert.True(await unconfirmedRequest.WaitCompleteAsync());
            Assert.Null(unconfirmedRequest.ResponseDownlink);
            Assert.True(unconfirmedRequest.ProcessingSucceeded);

            // fcnt up was updated
            Assert.Equal(startingPayloadFcnt, loRaDevice.FCntUp);
            Assert.Equal(0U, loRaDevice.FCntDown);

            if (startingPayloadFcnt != 0)
            {
                // Frame change flag will be set, only saving every 10 messages
                Assert.True(loRaDevice.HasFrameCountChanges);
            }

            Assert.Single(sentTelemetry);

            // sends confirmed message
            var confirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("200", fcnt: startingPayloadFcnt + 1);
            var confirmedMessageRxpk    = confirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var confirmedRequest        = this.CreateWaitableRequest(confirmedMessageRxpk, constantElapsedTime: TimeSpan.FromMilliseconds(300));

            messageProcessor.DispatchRequest(confirmedRequest);
            Assert.True(await confirmedRequest.WaitCompleteAsync());
            Assert.True(confirmedRequest.ProcessingSucceeded);
            Assert.NotNull(confirmedRequest.ResponseDownlink);
            Assert.NotNull(confirmedRequest.ResponseDownlink.Txpk);
            Assert.Equal(2, this.PacketForwarder.DownlinkMessages.Count);
            Assert.Equal(2, sentTelemetry.Count);
            var downstreamMessage = this.PacketForwarder.DownlinkMessages[1];

            // validates txpk according to eu region
            Assert.True(RegionManager.EU868.TryGetDownstreamChannelFrequency(confirmedMessageRxpk, out double frequency));
            Assert.Equal(frequency, downstreamMessage.Txpk.Freq);
            Assert.Equal("4/5", downstreamMessage.Txpk.Codr);
            Assert.False(downstreamMessage.Txpk.Imme);
            Assert.True(downstreamMessage.Txpk.Ipol);
            Assert.Equal("LORA", downstreamMessage.Txpk.Modu);

            // fcnt up was updated
            Assert.Equal(startingPayloadFcnt + 1, loRaDevice.FCntUp);
            Assert.Equal(1U, loRaDevice.FCntDown);

            // Frame change flag will be set, only saving every 10 messages
            Assert.True(loRaDevice.HasFrameCountChanges);

            // C2D message will be checked twice
            this.LoRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()), Times.Exactly(2));

            // has telemetry with both fcnt
            Assert.Single(sentTelemetry, (t) => t.Fcnt == startingPayloadFcnt);
            Assert.Single(sentTelemetry, (t) => t.Fcnt == (startingPayloadFcnt + 1));

            this.LoRaDeviceClient.VerifyAll();
        }
        public async Task When_Join_Fails_Due_To_Timeout_Second_Try_Should_Reuse_Cached_Device_Twin(string deviceGatewayID)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequest1    = simulatedDevice.CreateJoinRequest();

            // Create Rxpk
            var joinRequestRxpk1 = joinRequest1.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];

            var joinRequestDevNonce1 = ConversionHelper.ByteArrayToString(joinRequest1.DevNonce);
            var devAddr = string.Empty;
            var devEUI  = simulatedDevice.LoRaDevice.DeviceID;
            var appEUI  = simulatedDevice.LoRaDevice.AppEUI;

            var loRaDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict);

            // Device twin will be queried twice, 1st time will take 7 seconds, 2nd time 0.1 second
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEUI;
            twin.Properties.Desired[TwinProperty.AppEUI]        = simulatedDevice.LoRaDevice.AppEUI;
            twin.Properties.Desired[TwinProperty.AppKey]        = simulatedDevice.LoRaDevice.AppKey;
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            loRaDeviceClient.Setup(x => x.GetTwinAsync())
            .Returns(async() =>
            {
                await Task.Delay(TimeSpan.FromSeconds(7));
                return(twin);
            });

            // Device twin will be updated
            string afterJoinAppSKey = null;
            string afterJoinNwkSKey = null;
            string afterJoinDevAddr = null;

            loRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .Callback <TwinCollection>((updatedTwin) =>
            {
                afterJoinAppSKey = updatedTwin[TwinProperty.AppSKey];
                afterJoinNwkSKey = updatedTwin[TwinProperty.NwkSKey];
                afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr];
            })
            .ReturnsAsync(true);

            // Lora device api will be search by devices with matching deveui,
            var loRaDeviceApi = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);

            loRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, joinRequestDevNonce1))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            // using factory to create mock of
            var loRaDeviceFactory = new TestLoRaDeviceFactory(loRaDeviceClient.Object);

            var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, loRaDeviceApi.Object, loRaDeviceFactory);

            var frameCounterUpdateStrategyFactory = new LoRaDeviceFrameCounterUpdateStrategyFactory(this.ServerConfiguration.GatewayID, loRaDeviceApi.Object);

            var messageProcessor = new MessageProcessor(
                this.ServerConfiguration,
                deviceRegistry,
                frameCounterUpdateStrategyFactory,
                new LoRaPayloadDecoder());

            // 1st join request
            // Should fail
            var joinRequestDownlinkMessage1 = await messageProcessor.ProcessMessageAsync(joinRequestRxpk1);

            Assert.Null(joinRequestDownlinkMessage1);

            // 2nd attempt
            var joinRequest2         = simulatedDevice.CreateJoinRequest();
            var joinRequestRxpk2     = joinRequest2.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];
            var joinRequestDevNonce2 = LoRaTools.Utils.ConversionHelper.ByteArrayToString(joinRequest2.DevNonce);

            // setup response to this device search
            loRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, joinRequestDevNonce2))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            var joinRequestDownlinkMessage2 = await messageProcessor.ProcessMessageAsync(joinRequestRxpk2);

            Assert.NotNull(joinRequestDownlinkMessage2);
            var joinAccept = new LoRaPayloadJoinAccept(Convert.FromBase64String(joinRequestDownlinkMessage2.Txpk.Data), simulatedDevice.LoRaDevice.AppKey);

            Assert.Equal(joinAccept.DevAddr.ToArray(), ConversionHelper.StringToByteArray(afterJoinDevAddr));

            var devicesForDevAddr = deviceRegistry.InternalGetCachedDevicesForDevAddr(afterJoinDevAddr);

            Assert.Single(devicesForDevAddr); // should have the single device
            Assert.True(devicesForDevAddr.TryGetValue(devEUI, out var loRaDevice));

            Assert.Equal(simulatedDevice.AppKey, loRaDevice.AppKey);
            Assert.Equal(simulatedDevice.AppEUI, loRaDevice.AppEUI);
            Assert.Equal(afterJoinAppSKey, loRaDevice.AppSKey);
            Assert.Equal(afterJoinNwkSKey, loRaDevice.NwkSKey);
            Assert.Equal(afterJoinDevAddr, loRaDevice.DevAddr);
            if (deviceGatewayID == null)
            {
                Assert.Null(loRaDevice.GatewayID);
            }
            else
            {
                Assert.Equal(deviceGatewayID, loRaDevice.GatewayID);
            }

            // fcnt is restarted
            Assert.Equal(0, loRaDevice.FCntUp);
            Assert.Equal(0, loRaDevice.FCntDown);
            Assert.False(loRaDevice.HasFrameCountChanges);

            // searching the device should happen twice
            loRaDeviceApi.Verify(x => x.SearchAndLockForJoinAsync(ServerGatewayID, devEUI, appEUI, It.IsNotNull <string>()), Times.Exactly(2));

            // getting the device twin should happens once
            loRaDeviceClient.Verify(x => x.GetTwinAsync(), Times.Once());

            loRaDeviceClient.VerifyAll();
            loRaDeviceApi.VerifyAll();
        }
        public async Task When_Queueing_To_Multiple_Devices_With_Same_DevAddr_Should_Queue_To_Device_Matching_Mic(string deviceGatewayID)
        {
            var simulatedDevice1 = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID));

            var payload = simulatedDevice1.CreateUnconfirmedDataUpMessage("1234");
            payload.SerializeUplink(simulatedDevice1.AppSKey, simulatedDevice1.NwkSKey);

            var loRaDeviceClient1 = new Mock<ILoRaDeviceClient>(MockBehavior.Strict);
            loRaDeviceClient1.Setup(x => x.GetTwinAsync())
                .ReturnsAsync(simulatedDevice1.CreateABPTwin());

            var loraDevice1 = TestUtils.CreateFromSimulatedDevice(simulatedDevice1, loRaDeviceClient1.Object);
            var devAddr = loraDevice1.DevAddr;

            WaitableLoRaRequest request = null;
            var reqHandler1 = new Mock<ILoRaDataRequestHandler>(MockBehavior.Strict);
            reqHandler1.Setup(x => x.ProcessRequestAsync(It.IsNotNull<LoRaRequest>(), loraDevice1))
                .ReturnsAsync(new LoRaDeviceRequestProcessResult(loraDevice1, request));
            loraDevice1.SetRequestHandler(reqHandler1.Object);

            var simulatedDevice2 = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID));
            simulatedDevice2.LoRaDevice.DeviceID = "00000002";
            simulatedDevice2.LoRaDevice.NwkSKey = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
            var loRaDeviceClient2 = new Mock<ILoRaDeviceClient>(MockBehavior.Strict);
            loRaDeviceClient2.Setup(x => x.GetTwinAsync())
                .ReturnsAsync(simulatedDevice2.CreateABPTwin());
            var loraDevice2 = TestUtils.CreateFromSimulatedDevice(simulatedDevice2, loRaDeviceClient2.Object);

            // Api service: search devices async
            var iotHubDeviceInfo1 = new IoTHubDeviceInfo(devAddr, loraDevice1.DevEUI, string.Empty);
            var iotHubDeviceInfo2 = new IoTHubDeviceInfo(devAddr, loraDevice2.DevEUI, string.Empty);
            var apiService = new Mock<LoRaDeviceAPIServiceBase>();
            apiService.Setup(x => x.SearchByDevAddrAsync(devAddr))
                .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo[]
                {
                    iotHubDeviceInfo2,
                    iotHubDeviceInfo1,
                }));

            // Device factory: create 2 devices
            this.loraDeviceFactoryMock.Setup(x => x.Create(iotHubDeviceInfo1)).Returns(loraDevice1);
            this.loraDeviceFactoryMock.Setup(x => x.Create(iotHubDeviceInfo2)).Returns(loraDevice2);

            var target = new LoRaDeviceRegistry(this.serverConfiguration, this.cache, apiService.Object, this.loraDeviceFactoryMock.Object);
            request = new WaitableLoRaRequest(payload);
            target.GetLoRaRequestQueue(request).Queue(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingSucceeded);

            // Device was searched by DevAddr
            apiService.VerifyAll();

            // Device was created by factory
            this.loraDeviceFactoryMock.VerifyAll();

            // Both devices are in cache
            var devicesByDevAddrDictionary = target.InternalGetCachedDevicesForDevAddr(devAddr);
            Assert.NotNull(devicesByDevAddrDictionary);
            Assert.Equal(2, devicesByDevAddrDictionary.Count); // 2 devices with same devAddr exist in cache

            // find device 1
            Assert.True(devicesByDevAddrDictionary.TryGetValue(loraDevice1.DevEUI, out var actualCachedLoRaDevice1));
            Assert.Same(loraDevice1, actualCachedLoRaDevice1);
            Assert.True(loraDevice1.IsOurDevice);

            // find device 2
            Assert.True(devicesByDevAddrDictionary.TryGetValue(loraDevice2.DevEUI, out var actualCachedLoRaDevice2));
            Assert.Same(loraDevice2, actualCachedLoRaDevice2);
            Assert.True(loraDevice2.IsOurDevice);

            reqHandler1.VerifyAll();
            loRaDeviceClient1.VerifyAll();
            loRaDeviceClient2.VerifyAll();
        }
        public async Task When_Joining_Should_Save_Region_And_Preferred_Gateway(
            [CombinatorialValues(null, ServerGatewayID)] string deviceGatewayID,
            [CombinatorialValues(null, ServerGatewayID, "another-gateway")] string initialPreferredGatewayID,
            [CombinatorialValues(null, LoRaRegionType.EU868, LoRaRegionType.US915)] LoRaRegionType?initialLoRaRegion)
        {
            var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: deviceGatewayID));

            var customReportedProperties = new Dictionary <string, object>();

            // reported: { 'PreferredGateway': '' } -> if device is for multiple gateways and one initial was defined
            if (string.IsNullOrEmpty(deviceGatewayID) && !string.IsNullOrEmpty(initialPreferredGatewayID))
            {
                customReportedProperties[TwinProperty.PreferredGatewayID] = initialPreferredGatewayID;
            }

            if (initialLoRaRegion.HasValue)
            {
                customReportedProperties[TwinProperty.Region] = initialLoRaRegion.Value.ToString();
            }

            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(simDevice.CreateOTAATwin(reportedProperties: customReportedProperties));

            var shouldSavePreferredGateway = string.IsNullOrEmpty(deviceGatewayID) && initialPreferredGatewayID != ServerGatewayID;
            var shouldSaveRegion           = !initialLoRaRegion.HasValue || initialLoRaRegion.Value != LoRaRegionType.EU868;

            var savedAppSKey = string.Empty;
            var savedNwkSKey = string.Empty;
            var savedDevAddr = string.Empty;

            this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .ReturnsAsync(true)
            .Callback <TwinCollection>((t) =>
            {
                savedAppSKey = t[TwinProperty.AppSKey];
                savedNwkSKey = t[TwinProperty.NwkSKey];
                savedDevAddr = t[TwinProperty.DevAddr];

                Assert.NotEmpty(savedAppSKey);
                Assert.NotEmpty(savedNwkSKey);
                Assert.NotEmpty(savedDevAddr);

                if (shouldSaveRegion)
                {
                    Assert.Equal(LoRaRegionType.EU868.ToString(), t[TwinProperty.Region].Value as string);
                }
                else
                {
                    Assert.False(t.Contains(TwinProperty.Region));
                }

                // Only save preferred gateway if device does not have one assigned
                if (shouldSavePreferredGateway)
                {
                    Assert.Equal(this.ServerConfiguration.GatewayID, t[TwinProperty.PreferredGatewayID].Value as string);
                }
                else
                {
                    Assert.False(t.Contains(TwinProperty.PreferredGatewayID));
                }
            });

            this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, simDevice.DevEUI, simDevice.AppEUI, It.IsNotNull <string>()))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simDevice.DevAddr, simDevice.DevEUI, "123").AsList()));

            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewMemoryCache(), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            var messageDispatcher = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            var joinRxpk    = simDevice.CreateJoinRequest().SerializeUplink(simDevice.AppKey).Rxpk[0];
            var joinRequest = this.CreateWaitableRequest(joinRxpk);

            messageDispatcher.DispatchRequest(joinRequest);
            Assert.True(await joinRequest.WaitCompleteAsync());
            Assert.True(joinRequest.ProcessingSucceeded);

            var devices = deviceRegistry.InternalGetCachedDevicesForDevAddr(savedDevAddr);

            Assert.True(devices.TryGetValue(simDevice.DevEUI, out var loRaDevice));

            Assert.Equal(LoRaDeviceClassType.C, loRaDevice.ClassType);
            if (string.IsNullOrEmpty(simDevice.LoRaDevice.GatewayID))
            {
                Assert.Equal(this.ServerConfiguration.GatewayID, loRaDevice.PreferredGatewayID);
            }
            else
            {
                Assert.Empty(loRaDevice.PreferredGatewayID);
            }

            Assert.Equal(LoRaRegionType.EU868, loRaDevice.LoRaRegion);

            this.LoRaDeviceApi.VerifyAll();
            this.LoRaDeviceClient.VerifyAll();
        }
        public async Task When_First_Join_Fails_Due_To_Slow_Twin_Update_Retry_Second_Attempt_Should_Succeed(string deviceGatewayID)
        {
            var simulatedDevice     = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequestPayload1 = simulatedDevice.CreateJoinRequest();
            var joinRequestPayload2 = simulatedDevice.CreateJoinRequest();

            // Create Rxpk
            var joinRequestRxpk1 = joinRequestPayload1.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];
            var joinRequestRxpk2 = joinRequestPayload2.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];

            var joinRequestDevNonce1 = joinRequestPayload1.GetDevNonceAsString();
            var joinRequestDevNonce2 = joinRequestPayload2.GetDevNonceAsString();
            var devAddr = string.Empty;
            var devEUI  = simulatedDevice.LoRaDevice.DeviceID;
            var appEUI  = simulatedDevice.LoRaDevice.AppEUI;

            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEUI;
            twin.Properties.Desired[TwinProperty.AppEUI]        = simulatedDevice.LoRaDevice.AppEUI;
            twin.Properties.Desired[TwinProperty.AppKey]        = simulatedDevice.LoRaDevice.AppKey;
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(twin);

            // Device twin will be updated
            string afterJoin1AppSKey = null;
            string afterJoin1NwkSKey = null;
            string afterJoin1DevAddr = null;
            string afterJoin2AppSKey = null;
            string afterJoin2NwkSKey = null;
            string afterJoin2DevAddr = null;

            this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .ReturnsAsync(true, TimeSpan.FromSeconds(10))
            .Callback <TwinCollection>((updatedTwin) =>
            {
                afterJoin1AppSKey = updatedTwin[TwinProperty.AppSKey];
                afterJoin1NwkSKey = updatedTwin[TwinProperty.NwkSKey];
                afterJoin1DevAddr = updatedTwin[TwinProperty.DevAddr];

                // update setup for no delay
                this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
                .ReturnsAsync(true)
                .Callback <TwinCollection>((updatedTwin2) =>
                {
                    afterJoin2AppSKey = updatedTwin2[TwinProperty.AppSKey];
                    afterJoin2NwkSKey = updatedTwin2[TwinProperty.NwkSKey];
                    afterJoin2DevAddr = updatedTwin2[TwinProperty.DevAddr];
                });
            });

            // Lora device api will be search by devices with matching deveui,
            this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, joinRequestDevNonce1))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, joinRequestDevNonce2))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewMemoryCache(), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            var messageProcessor = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            var joinRequest1 = this.CreateWaitableRequest(joinRequestRxpk1);

            messageProcessor.DispatchRequest(joinRequest1);
            await Task.Delay(TimeSpan.FromSeconds(7));

            var joinRequest2 = this.CreateWaitableRequest(joinRequestRxpk2);

            messageProcessor.DispatchRequest(joinRequest2);

            await Task.WhenAll(joinRequest1.WaitCompleteAsync(), joinRequest2.WaitCompleteAsync());

            Assert.True(joinRequest1.ProcessingFailed);
            Assert.Null(joinRequest1.ResponseDownlink);
            Assert.True(joinRequest2.ProcessingSucceeded);
            Assert.NotNull(joinRequest2.ResponseDownlink);
            Assert.Single(this.PacketForwarder.DownlinkMessages);

            Assert.Empty(deviceRegistry.InternalGetCachedDevicesForDevAddr(afterJoin1DevAddr));
            var devicesInDevAddr2 = deviceRegistry.InternalGetCachedDevicesForDevAddr(afterJoin2DevAddr);

            Assert.NotEmpty(devicesInDevAddr2);
            Assert.True(devicesInDevAddr2.TryGetValue(devEUI, out var loRaDevice));
            Assert.True(loRaDevice.IsOurDevice);
            Assert.Equal(afterJoin2DevAddr, loRaDevice.DevAddr);
            Assert.Equal(afterJoin2NwkSKey, loRaDevice.NwkSKey);
            Assert.Equal(afterJoin2AppSKey, loRaDevice.AppSKey);

            // get twin should happen only once
            this.LoRaDeviceClient.Verify(x => x.GetTwinAsync(), Times.Once());
            this.LoRaDeviceClient.VerifyAll();
            this.LoRaDeviceApi.VerifyAll();
        }
Пример #22
0
        public async Task When_Device_Is_Found_In_Api_Should_Update_Twin_And_Return()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: this.ServerConfiguration.GatewayID));

            simulatedDevice.LoRaDevice.NwkSKey = string.Empty;
            simulatedDevice.LoRaDevice.AppSKey = string.Empty;
            var joinRequest = simulatedDevice.CreateJoinRequest();

            // Create Rxpk
            var rxpk = joinRequest.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];

            var devNonce = ConversionHelper.ByteArrayToString(joinRequest.DevNonce);
            var devEUI   = simulatedDevice.LoRaDevice.DeviceID;
            var appEUI   = simulatedDevice.LoRaDevice.AppEUI;

            this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(It.IsNotNull <string>(), devEUI, appEUI, devNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(string.Empty, devEUI, "123").AsList()));

            // Ensure that the device twin was updated
            this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.Is <TwinCollection>((t) =>
                                                                                                    t.Contains(TwinProperty.DevAddr) && t.Contains(TwinProperty.FCntDown))))
            .ReturnsAsync(true);

            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(simulatedDevice.CreateOTAATwin());

            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewMemoryCache(), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageProcessor = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            var request = this.CreateWaitableRequest(rxpk);

            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.NotNull(request.ResponseDownlink);
            Assert.Single(this.PacketForwarder.DownlinkMessages);

            var pktFwdMessage = this.PacketForwarder.DownlinkMessages[0];

            Assert.NotNull(pktFwdMessage.Txpk);
            var joinAccept = new LoRaPayloadJoinAccept(Convert.FromBase64String(pktFwdMessage.Txpk.Data), simulatedDevice.AppKey);

            var joinedDeviceDevAddr = ConversionHelper.ByteArrayToString(joinAccept.DevAddr);
            var cachedDevices       = deviceRegistry.InternalGetCachedDevicesForDevAddr(joinedDeviceDevAddr);

            Assert.Single(cachedDevices);
            Assert.True(cachedDevices.TryGetValue(devEUI, out var loRaDevice));
            Assert.Equal(joinAccept.DevAddr.ToArray(), this.ByteArray(loRaDevice.DevAddr).ToArray());

            // Device properties were set with the computes values of the join operation
            Assert.Equal(joinAccept.AppNonce.ToArray(), this.ReversedByteArray(loRaDevice.AppNonce).ToArray());
            Assert.NotEmpty(loRaDevice.NwkSKey);
            Assert.NotEmpty(loRaDevice.AppSKey);
            Assert.True(loRaDevice.IsOurDevice);

            // Device frame counts were reset
            Assert.Equal(0U, loRaDevice.FCntDown);
            Assert.Equal(0U, loRaDevice.FCntUp);
            Assert.False(loRaDevice.HasFrameCountChanges);

            // Twin property were updated
            this.LoRaDeviceClient.VerifyAll();
            this.LoRaDeviceApi.VerifyAll();
        }
Пример #23
0
        public async Task When_First_Join_Fails_Due_To_Slow_Twin_Update_Retry_Second_Attempt_Should_Succeed(string deviceGatewayID)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequest1    = simulatedDevice.CreateJoinRequest();
            var joinRequest2    = simulatedDevice.CreateJoinRequest();

            // Create Rxpk
            var joinRequestRxpk1 = joinRequest1.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];
            var joinRequestRxpk2 = joinRequest2.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];

            var joinRequestDevNonce1 = joinRequest1.GetDevNonceAsString();
            var joinRequestDevNonce2 = joinRequest2.GetDevNonceAsString();
            var devAddr = string.Empty;
            var devEUI  = simulatedDevice.LoRaDevice.DeviceID;
            var appEUI  = simulatedDevice.LoRaDevice.AppEUI;

            var loRaDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict);

            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEUI;
            twin.Properties.Desired[TwinProperty.AppEUI]        = simulatedDevice.LoRaDevice.AppEUI;
            twin.Properties.Desired[TwinProperty.AppKey]        = simulatedDevice.LoRaDevice.AppKey;
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            loRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(twin);

            // Device twin will be updated
            string afterJoin1AppSKey = null;
            string afterJoin1NwkSKey = null;
            string afterJoin1DevAddr = null;
            string afterJoin2AppSKey = null;
            string afterJoin2NwkSKey = null;
            string afterJoin2DevAddr = null;
            var    isFirstTwinUpdate = true;

            loRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .Callback <TwinCollection>((updatedTwin) =>
            {
                if (isFirstTwinUpdate)
                {
                    afterJoin1AppSKey = updatedTwin[TwinProperty.AppSKey];
                    afterJoin1NwkSKey = updatedTwin[TwinProperty.NwkSKey];
                    afterJoin1DevAddr = updatedTwin[TwinProperty.DevAddr];

                    Thread.Sleep(TimeSpan.FromSeconds(10));
                    isFirstTwinUpdate = false;
                }
                else
                {
                    afterJoin2AppSKey = updatedTwin[TwinProperty.AppSKey];
                    afterJoin2NwkSKey = updatedTwin[TwinProperty.NwkSKey];
                    afterJoin2DevAddr = updatedTwin[TwinProperty.DevAddr];
                }
            })
            .ReturnsAsync(true);

            // Lora device api will be search by devices with matching deveui,
            var loRaDeviceApi = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);

            loRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, joinRequestDevNonce1))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            loRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, joinRequestDevNonce2))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            // using factory to create mock of
            var loRaDeviceFactory = new TestLoRaDeviceFactory(loRaDeviceClient.Object);

            var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, loRaDeviceApi.Object, loRaDeviceFactory);

            var frameCounterUpdateStrategyFactory = new LoRaDeviceFrameCounterUpdateStrategyFactory(this.ServerConfiguration.GatewayID, loRaDeviceApi.Object);

            var messageProcessor = new MessageProcessor(
                this.ServerConfiguration,
                deviceRegistry,
                frameCounterUpdateStrategyFactory,
                new LoRaPayloadDecoder());

            var joinRequestTask1 = messageProcessor.ProcessMessageAsync(joinRequestRxpk1);

            await Task.Delay(TimeSpan.FromSeconds(7));

            var joinRequestTask2 = messageProcessor.ProcessMessageAsync(joinRequestRxpk2);

            await Task.WhenAll(joinRequestTask1, joinRequestTask2);

            Assert.Null(joinRequestTask1.Result);
            Assert.NotNull(joinRequestTask2.Result);

            Assert.Empty(deviceRegistry.InternalGetCachedDevicesForDevAddr(afterJoin1DevAddr));
            var devicesInDevAddr2 = deviceRegistry.InternalGetCachedDevicesForDevAddr(afterJoin2DevAddr);

            Assert.NotEmpty(devicesInDevAddr2);
            Assert.True(devicesInDevAddr2.TryGetValue(devEUI, out var loRaDevice));
            Assert.True(loRaDevice.IsOurDevice);
            Assert.Equal(afterJoin2DevAddr, loRaDevice.DevAddr);
            Assert.Equal(afterJoin2NwkSKey, loRaDevice.NwkSKey);
            Assert.Equal(afterJoin2AppSKey, loRaDevice.AppSKey);

            // get twin should happen only once
            loRaDeviceClient.Verify(x => x.GetTwinAsync(), Times.Once());
            loRaDeviceClient.VerifyAll();
            loRaDeviceApi.VerifyAll();
        }
        public async Task When_Device_Prefers_Second_Window_Should_Send_Downstream_In_Second_Window()
        {
            const int PayloadFcnt           = 10;
            const int InitialDeviceFcntUp   = 9;
            const int InitialDeviceFcntDown = 20;

            var simulatedDevice = new SimulatedDevice(
                TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID),
                frmCntUp: InitialDeviceFcntUp,
                frmCntDown: InitialDeviceFcntDown);

            var devAddr = simulatedDevice.DevAddr;
            var devEUI  = simulatedDevice.DevEUI;

            var loraDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict);

            // Will get twin to initialize LoRaDevice
            var deviceTwin = TestUtils.CreateABPTwin(
                simulatedDevice,
                desiredProperties: new Dictionary <string, object>
            {
                { TwinProperty.PreferredWindow, 2 }
            });

            loraDeviceClient.Setup(x => x.GetTwinAsync()).ReturnsAsync(deviceTwin);

            loraDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

            loraDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .ReturnsAsync(true);

            var cloudToDeviceMessage = new Message(Encoding.UTF8.GetBytes("c2d"));

            cloudToDeviceMessage.Properties[MessageProcessor.FPORT_MSG_PROPERTY_KEY] = "1";
            loraDeviceClient.SetupSequence(x => x.ReceiveAsync(It.IsAny <TimeSpan>()))
            .ReturnsAsync(cloudToDeviceMessage)
            .Returns(this.EmptyAdditionalMessageReceiveAsync);     // 2nd cloud to device message does not return anything

            loraDeviceClient.Setup(x => x.CompleteAsync(cloudToDeviceMessage))
            .ReturnsAsync(true);

            var payloadDecoder = new Mock <ILoRaPayloadDecoder>();

            var loRaDeviceAPI = new Mock <LoRaDeviceAPIServiceBase>();

            loRaDeviceAPI.Setup(x => x.SearchByDevAddrAsync(devAddr))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "adad").AsList()));

            var loRaDeviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, new MemoryCache(new MemoryCacheOptions()), loRaDeviceAPI.Object, new TestLoRaDeviceFactory(loraDeviceClient.Object));

            // Send to message processor
            var messageProcessor = new MessageProcessor(
                this.ServerConfiguration,
                loRaDeviceRegistry,
                new LoRaDeviceFrameCounterUpdateStrategyFactory(this.ServerConfiguration.GatewayID, loRaDeviceAPI.Object),
                new LoRaPayloadDecoder());

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt);
            var rxpk    = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var actual  = await messageProcessor.ProcessMessageAsync(rxpk);

            // Expectations
            // 1. Message was sent to IoT Hub
            loraDeviceClient.VerifyAll();

            // 2. Single gateway frame counter strategy was used
            this.FrameCounterUpdateStrategyFactory.VerifyAll();

            // 3. Return is downstream message
            Assert.NotNull(actual);
            Assert.IsType <DownlinkPktFwdMessage>(actual);
            var downlinkMessage = (DownlinkPktFwdMessage)actual;
            var euRegion        = RegionFactory.CreateEU868Region();

            // Ensure we are using second window frequency
            Assert.Equal(euRegion.RX2DefaultReceiveWindows.frequency, actual.Txpk.Freq);

            // Ensure we are using second window datr
            Assert.Equal(euRegion.DRtoConfiguration[euRegion.RX2DefaultReceiveWindows.dr].configuration, actual.Txpk.Datr);

            // Ensure tmst was computed to 2 seconds (2 windows in Europe)
            Assert.Equal(2000000, actual.Txpk.Tmst);

            // Get the device from registry
            var deviceDictionary = loRaDeviceRegistry.InternalGetCachedDevicesForDevAddr(devAddr);

            Assert.True(deviceDictionary.TryGetValue(simulatedDevice.DevEUI, out var loRaDevice));
            var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data));

            payloadDataDown.PerformEncryption(loRaDevice.AppSKey);
            Assert.Equal(payloadDataDown.DevAddr.ToArray(), ConversionHelper.StringToByteArray(loRaDevice.DevAddr));
            Assert.False(payloadDataDown.IsConfirmed());
            Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType);

            // 4. Frame counter up was updated
            Assert.Equal(PayloadFcnt, loRaDevice.FCntUp);

            // 5. Frame counter down is updated
            var expectedFcntDown = InitialDeviceFcntDown + 10 + 1; // adding 10 as buffer when creating a new device instance

            Assert.Equal(expectedFcntDown, loRaDevice.FCntDown);
            Assert.Equal(expectedFcntDown, payloadDataDown.GetFcnt());

            // 6. Frame count has no pending changes
            Assert.False(loRaDevice.HasFrameCountChanges);
        }
        public async Task When_ABP_Sends_Upstream_Followed_By_DirectMethod_Should_Send_Upstream_And_Downstream(string deviceGatewayID, uint fcntDownFromTwin, uint fcntDelta)
        {
            const uint payloadFcnt = 2; // to avoid relax mode reset

            var simDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID, deviceClassType: 'c'), frmCntDown: fcntDownFromTwin);

            this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .ReturnsAsync(true);

            var twin = simDevice.CreateABPTwin(reportedProperties: new Dictionary <string, object>
            {
                { TwinProperty.Region, LoRaRegionType.EU868.ToString() }
            });

            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync())
            .ReturnsAsync(twin);

            this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

            this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            this.LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simDevice.DevAddr))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simDevice.DevAddr, simDevice.DevEUI, "123").AsList()));

            if (deviceGatewayID == null)
            {
                this.LoRaDeviceApi.Setup(x => x.ExecuteFunctionBundlerAsync(simDevice.DevEUI, It.IsNotNull <FunctionBundlerRequest>()))
                .ReturnsAsync(new FunctionBundlerResult());
            }

            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewMemoryCache(), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            var messageDispatcher = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            var request = this.CreateWaitableRequest(simDevice.CreateUnconfirmedMessageUplink("1", fcnt: payloadFcnt).Rxpk[0]);

            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingSucceeded);

            // wait until cache has been updated
            await Task.Delay(50);

            // Adds fcntdown to device, simulating multiple downstream calls
            Assert.True(deviceRegistry.InternalGetCachedDevicesForDevAddr(simDevice.DevAddr).TryGetValue(simDevice.DevEUI, out var loRaDevice));
            loRaDevice.SetFcntDown(fcntDelta + loRaDevice.FCntDown);

            var classCSender = new DefaultClassCDevicesMessageSender(
                this.ServerConfiguration,
                deviceRegistry,
                this.PacketForwarder,
                this.FrameCounterUpdateStrategyProvider);

            var c2d = new ReceivedLoRaCloudToDeviceMessage()
            {
                DevEUI    = simDevice.DevEUI,
                MessageId = Guid.NewGuid().ToString(),
                Payload   = "aaaa",
                Fport     = 18,
            };

            var expectedFcntDown = fcntDownFromTwin + Constants.MAX_FCNT_UNSAVED_DELTA + fcntDelta;

            if (string.IsNullOrEmpty(deviceGatewayID))
            {
                this.LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(simDevice.DevEUI, fcntDownFromTwin + fcntDelta, 0, this.ServerConfiguration.GatewayID))
                .ReturnsAsync((ushort)expectedFcntDown);
            }

            Assert.True(await classCSender.SendAsync(c2d));
            Assert.Single(this.PacketForwarder.DownlinkMessages);
            var downstreamMsg = this.PacketForwarder.DownlinkMessages[0];

            byte[] downstreamPayloadBytes = Convert.FromBase64String(downstreamMsg.Txpk.Data);
            var    downstreamPayload      = new LoRaPayloadData(downstreamPayloadBytes);

            Assert.Equal(expectedFcntDown, downstreamPayload.GetFcnt());
            Assert.Equal(c2d.Fport, downstreamPayload.GetFPort());
            Assert.Equal(downstreamPayload.DevAddr.ToArray(), ConversionHelper.StringToByteArray(simDevice.DevAddr));
            var decryptedPayload = downstreamPayload.GetDecryptedPayload(simDevice.AppSKey);

            Assert.Equal(c2d.Payload, Encoding.UTF8.GetString(decryptedPayload));

            Assert.Equal(expectedFcntDown, loRaDevice.FCntDown);
            Assert.Equal(payloadFcnt, loRaDevice.FCntUp);

            Assert.False(loRaDevice.HasFrameCountChanges);

            this.LoRaDeviceApi.VerifyAll();
            this.LoRaDeviceClient.VerifyAll();
        }
Пример #26
0
        public async Task When_Getting_C2D_Message_Fails_To_Resolve_Fcnt_Down_Should_Drop_Message_And_Return_Null()
        {
            const int initialFcntDown = 5;
            const int initialFcntUp   = 21;
            const int payloadFcnt     = 23;

            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: null));

            simulatedDevice.FrmCntUp   = initialFcntUp;
            simulatedDevice.FrmCntDown = initialFcntDown;

            var devEUI  = simulatedDevice.LoRaDevice.DeviceID;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr;

            // message will be sent
            var loRaDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict);

            loRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

            var cloudToDeviceMessage = new Message();

            cloudToDeviceMessage.Properties.Add("fport", "1");
            // C2D message will be retrieved
            loRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>()))
            .ReturnsAsync(cloudToDeviceMessage);

            // C2D message will be abandonned
            loRaDeviceClient.Setup(x => x.AbandonAsync(cloudToDeviceMessage))
            .ReturnsAsync(true);

            // Lora device api
            var loRaDeviceApi = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);

            // getting the fcnt down will return 0!
            loRaDeviceApi.Setup(x => x.NextFCntDownAsync(devEUI, initialFcntDown, payloadFcnt, this.ServerConfiguration.GatewayID))
            .ReturnsAsync((ushort)0);

            // using factory to create mock of
            var loRaDeviceFactory = new TestLoRaDeviceFactory(loRaDeviceClient.Object);

            var memoryCache  = new MemoryCache(new MemoryCacheOptions());
            var cachedDevice = TestUtils.CreateFromSimulatedDevice(simulatedDevice, loRaDeviceClient.Object);

            var devEUIDeviceDict = new DevEUIToLoRaDeviceDictionary();

            devEUIDeviceDict.TryAdd(devEUI, cachedDevice);
            memoryCache.Set(devAddr, devEUIDeviceDict);

            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, loRaDeviceApi.Object, loRaDeviceFactory);

            var frameCounterUpdateStrategyFactory = new LoRaDeviceFrameCounterUpdateStrategyFactory(this.ServerConfiguration.GatewayID, loRaDeviceApi.Object);

            // Send to message processor
            var messageProcessor = new MessageProcessor(
                this.ServerConfiguration,
                deviceRegistry,
                frameCounterUpdateStrategyFactory,
                new LoRaPayloadDecoder());

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("hello", fcnt: payloadFcnt);
            var rxpk = unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var unconfirmedMessageResult = await messageProcessor.ProcessMessageAsync(rxpk);

            Assert.Null(unconfirmedMessageResult);

            var cachedDevices = deviceRegistry.InternalGetCachedDevicesForDevAddr(simulatedDevice.DevAddr);

            Assert.True(cachedDevices.TryGetValue(devEUI, out var loRaDevice));
            // fcnt down did not change
            Assert.Equal(initialFcntDown, loRaDevice.FCntDown);

            // fcnt up changed
            Assert.Equal(unconfirmedMessagePayload.GetFcnt(), loRaDevice.FCntUp);

            loRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsAny <TimeSpan>()), Times.Once());
            loRaDeviceClient.Verify(x => x.AbandonAsync(It.IsAny <Message>()), Times.Once());

            loRaDeviceClient.VerifyAll();
            loRaDeviceApi.VerifyAll();
        }