示例#1
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))
            {
                FrmCntDown = initialFcntDown,
                FrmCntUp   = initialFcntUp
            };

            var devEui  = simulatedDevice.LoRaDevice.DevEui;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr;

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

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

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

            DeviceCache.Register(device);
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

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

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("hello", fcnt: payloadFcnt);

            using var request = CreateWaitableRequest(unconfirmedMessagePayload);
            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
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, 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();
        }
        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.DevEui, "pk");

            LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(It.IsAny <DevAddr>()))
            .ReturnsAsync(new SearchDevicesResult(iotHubDeviceInfo.AsList()));

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

            using var cache          = NewMemoryCache();
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

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

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 2);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload, 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);

            Assert.Equal(1, DeviceCache.RegistrationCount(simulatedDevice.DevAddr.Value));
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, out var cachedDevice));
            Assert.True(cachedDevice.IsOurDevice);
            Assert.Equal(Constants.MaxFcntUnsavedDelta - 1U, cachedDevice.FCntDown);
            Assert.Equal(payload.Fcnt, (ushort)cachedDevice.FCntUp);

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

            // Device was created by factory
            LoRaDeviceClient.VerifyAll();
        }
示例#3
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 apiService       = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);
            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DevEui, "pk")
            {
                GatewayId = "another-gateway",
                NwkSKey   = simulatedDevice.NwkSKey.Value
            };

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

            var deviceFactory = new TestLoRaDeviceFactory(LoRaDeviceClient.Object, DeviceCache, ConnectionManager);

            using var target = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, deviceFactory, DeviceCache);

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

            using var request1 = WaitableLoRaRequest.Create(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);

            using var request2 = WaitableLoRaRequest.Create(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.IsAny <DevAddr>()), Times.Once());

            // Device should not be connected
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceClient.Verify(x => x.GetTwinAsync(CancellationToken.None), Times.Never());
            LoRaDeviceClient.Verify(x => x.Disconnect(), Times.Never());

            // device is in cache
            Assert.True(DeviceCache.TryGetForPayload(request1.Payload, out var loRaDevice));
            Assert.False(loRaDevice.IsOurDevice);
        }
示例#4
0
        public async Task When_Device_Is_Assigned_To_Another_Gateway_After_No_Connection_Should_Be_Established()
        {
            const string gatewayId       = "another-gateway";
            var          simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: gatewayId));

            var apiService       = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);
            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DevEui, "pk")
            {
                NwkSKey = simulatedDevice.NwkSKey.Value, GatewayId = gatewayId
            };

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

            var deviceFactory = new TestLoRaDeviceFactory(LoRaDeviceClient.Object, DeviceCache, ConnectionManager);

            using var target = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, deviceFactory, DeviceCache);

            // setup 2 requests - ensure the cache is validated before fetching from the function
            var requests = Enumerable.Range(1, 2).Select((n) =>
            {
                var payload = simulatedDevice.CreateUnconfirmedDataUpMessage(n.ToString(CultureInfo.InvariantCulture), fcnt: (uint)n + 10);
                var request = WaitableLoRaRequest.Create(payload);
                target.GetLoRaRequestQueue(request).Queue(request);
                return(request);
            }
                                                         ).ToList();

            foreach (var request in requests)
            {
                Assert.True(await request.WaitCompleteAsync());
                Assert.True(request.ProcessingFailed);
                Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request.ProcessingFailedReason);
            }

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

            LoRaDeviceClient.Verify(x => x.GetTwinAsync(CancellationToken.None), Times.Never());

            // device is in cache
            Assert.True(DeviceCache.TryGetForPayload(requests.First().Payload, out var cachedLoRaDevice));
            Assert.False(cachedLoRaDevice.IsOurDevice);
        }
示例#5
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");

            var apiService       = new Mock <LoRaDeviceAPIServiceBase>();
            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DevEui, "pk")
            {
                GatewayId = deviceGatewayID
            };

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

            // device will be initialized
            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None))
            .ReturnsAsync(simulatedDevice.CreateABPTwin());

            using var request = WaitableLoRaRequest.Create(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(LoRaDeviceClient.Object, requestHandler.Object, DeviceCache, ConnectionManager);

            using var target = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, deviceFactory, DeviceCache);
            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
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, out var actualCachedLoRaDevice));

            // request was handled
            requestHandler.VerifyAll();
        }
        public async Task When_ABP_Sends_Upstream_Followed_By_DirectMethod_Should_Send_Upstream_And_Downstream(string deviceGatewayID, uint fcntDownFromTwin, uint fcntDelta, Region region)
        {
            const uint payloadFcnt = 2; // to avoid relax mode reset

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

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

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

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

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

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

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

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

            using var cache          = NewMemoryCache();
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);
            var payloadData = simDevice.CreateUnconfirmedDataUpMessage("1", fcnt: payloadFcnt);

            using var request = CreateWaitableRequest(payloadData, region: region);
            request.SetStationEui(new StationEui(ulong.MaxValue));
            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(DeviceCache.TryGetForPayload(request.Payload, out var loRaDevice));
            loRaDevice.SetFcntDown(fcntDelta + loRaDevice.FCntDown);

            var classCSender = new DefaultClassCDevicesMessageSender(
                ServerConfiguration,
                deviceRegistry,
                DownstreamMessageSender,
                FrameCounterUpdateStrategyProvider,
                new TestOutputLogger <DefaultClassCDevicesMessageSender>(this.testOutputHelper),
                TestMeter.Instance);

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

            var expectedFcntDown = fcntDownFromTwin + Constants.MaxFcntUnsavedDelta + fcntDelta;

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

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

            var downstreamPayloadBytes = downstreamMsg.Data;
            var downstreamPayload      = new LoRaPayloadData(downstreamPayloadBytes);

            Assert.Equal(expectedFcntDown, downstreamPayload.Fcnt);
            Assert.Equal(c2d.Fport, downstreamPayload.Fport);
            Assert.Equal(downstreamPayload.DevAddr, simDevice.DevAddr);
            var decryptedPayload = downstreamPayload.GetDecryptedPayload(simDevice.AppSKey.Value);

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

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

            LoRaDeviceApi.VerifyAll();
            LoRaDeviceClient.VerifyAll();
        }
        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.DevEui;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr.Value;

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

            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, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default));
            });

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

            initialTwin.Properties.Desired[TwinProperty.DevEUI]  = devEui.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppEui]  = simulatedDevice.LoRaDevice.AppEui?.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppKey]  = simulatedDevice.LoRaDevice.AppKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.NwkSKey] = simulatedDevice.LoRaDevice.NwkSKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppSKey] = simulatedDevice.LoRaDevice.AppSKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.DevAddr] = devAddr.ToString();
            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(CancellationToken.None))
            .Returns(() =>
            {
                var duration = parallelTestConfiguration.LoadTwinDuration.Next();
                Console.WriteLine($"{nameof(looseDeviceClient.Object.GetTwinAsync)} sleeping for {duration}");
                return(Task.Delay(duration)
                       .ContinueWith(_ => initialTwin, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default));
            });

            // 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>(), It.IsAny <CancellationToken>()))
                .Returns <TwinCollection, CancellationToken>((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, CancellationToken.None)
                           .ContinueWith((a) => true, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default));
                });
            }

            if (expectedToSaveTwin && string.IsNullOrEmpty(parallelTestConfiguration.GatewayID))
            {
                LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(devEui, It.IsAny <uint>(), It.IsNotNull <string>()))
                .Returns(() =>
                {
                    var duration = parallelTestConfiguration.DeviceApiResetFcntDuration.Next();
                    Console.WriteLine($"{nameof(LoRaDeviceApi.Object.ABPFcntCacheResetAsync)} sleeping for {duration}");
                    return(Task.Delay(duration)
                           .ContinueWith((a) => true, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default));
                });
            }

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

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

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

            // sends unconfirmed message
            var unconfirmedMessage1 = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 1);
            var unconfirmedMessage2 = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 2);
            var unconfirmedMessage3 = simulatedDevice.CreateUnconfirmedDataUpMessage("3", fcnt: 3);

            var req1 = CreateWaitableRequest(unconfirmedMessage1, this.downstreamMessageSender);

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

            var req2 = CreateWaitableRequest(unconfirmedMessage2, this.downstreamMessageSender);

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

            var req3 = CreateWaitableRequest(unconfirmedMessage3, this.downstreamMessageSender);

            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(CancellationToken.None), Times.Exactly(1));
            if (expectedToSaveTwin)
            {
                looseDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Exactly(1));
            }

            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
            Assert.True(DeviceCache.TryGetForPayload(req1.Payload, 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();
            LoRaDeviceApi.VerifyAll();
        }
示例#8
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.DevEui;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr.Value;

            // message will be sent
            LoRaDeviceTelemetry loRaDeviceTelemetry = null;

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

            // C2D message will be checked
            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.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppEui]  = simulatedDevice.LoRaDevice.AppEui?.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppKey]  = simulatedDevice.LoRaDevice.AppKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.NwkSKey] = simulatedDevice.LoRaDevice.NwkSKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppSKey] = simulatedDevice.LoRaDevice.AppSKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.DevAddr] = devAddr.ToString();
            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;
            }

            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).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)
            {
                LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
                .Callback <TwinCollection, CancellationToken>((t, _) =>
                {
                    fcntUpSavedInTwin   = (int)t[TwinProperty.FCntUp];
                    fcntDownSavedInTwin = (int)t[TwinProperty.FCntDown];
                })
                .ReturnsAsync(true);
            }

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

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

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

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

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("hello", fcnt: payloadFcntUp);

            using var request = CreateWaitableRequest(unconfirmedMessagePayload);
            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
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, 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!
            }
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
示例#9
0
        public async Task When_Takes_Too_Long_Processing_C2D_Should_Abandon_Message()
        {
            const uint PayloadFcnt           = 10;
            const uint InitialDeviceFcntUp   = 9;
            const uint InitialDeviceFcntDown = 20;

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

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

            // Will get twin to initialize LoRaDevice
            var deviceTwin = TestUtils.CreateABPTwin(simulatedDevice);

            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(deviceTwin);
            this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>())).ReturnsAsync(true);

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

            using var cloudToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage()
                  {
                      Payload = "c2d", Fport = TestPort
                  }
                  .CreateMessage();

            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>()))
            .ReturnsAsync(cloudToDeviceMessage);

            LoRaDeviceClient.Setup(x => x.AbandonAsync(cloudToDeviceMessage))
            .ReturnsAsync(true);

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

            using var cache = NewMemoryCache();
            using var loRaDeviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      loRaDeviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt);

            using var request = WaitableLoRaRequest.Create(TestUtils.GenerateTestRadioMetadata(), DownstreamMessageSender,
                                                           inTimeForC2DMessageCheck: true,
                                                           inTimeForAdditionalMessageCheck: false,
                                                           inTimeForDownlinkDelivery: false,
                                                           payload);
            request.SetRegion(this.DefaultRegion);

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

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

            // 2. No downstream message
            Assert.Null(request.ResponseDownlink);
            Assert.True(request.ProcessingSucceeded);
            Assert.Empty(DownstreamMessageSender.DownlinkMessages);

            // 3. Device FcntDown did change
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, out var loRaDevice));
            Assert.Equal(InitialDeviceFcntDown + Constants.MaxFcntUnsavedDelta, loRaDevice.FCntDown);
        }
示例#10
0
        public async Task When_Device_Prefers_Second_Window_Should_Send_Downstream_In_Second_Window()
        {
            const uint PayloadFcnt           = 10;
            const uint InitialDeviceFcntUp   = 9;
            const uint InitialDeviceFcntDown = 20;

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

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

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

            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(deviceTwin);

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

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

            using var cloudToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage()
                  {
                      Payload = "c2d", Fport = TestPort
                  }
                  .CreateMessage();

            LoRaDeviceClient.SetupSequence(x => x.ReceiveAsync(It.IsAny <TimeSpan>()))
            .ReturnsAsync(cloudToDeviceMessage)
            .Returns(EmptyAdditionalMessageReceiveAsync);     // 2nd cloud to device message does not return anything

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

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

            using var cache = NewMemoryCache();
            using var loRaDeviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      loRaDeviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt);

            using var request = CreateWaitableRequest(payload);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

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

            // 3. Return is downstream message
            Assert.True(request.ProcessingSucceeded);
            Assert.NotNull(request.ResponseDownlink);
            Assert.Single(DownstreamMessageSender.DownlinkMessages);
            var downlinkMessage = DownstreamMessageSender.DownlinkMessages.First();

            TestUtils.CheckDRAndFrequencies(request, downlinkMessage, true);

            // Get the device from cache
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, out var loRaDevice));
            var payloadDataDown = new LoRaPayloadData(downlinkMessage.Data);

            payloadDataDown.Serialize(loRaDevice.AppSKey.Value);
            Assert.Equal(payloadDataDown.DevAddr, loRaDevice.DevAddr);
            Assert.False(payloadDataDown.IsConfirmed);
            Assert.Equal(MacMessageType.UnconfirmedDataDown, payloadDataDown.MessageType);

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

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

            Assert.Equal(expectedFcntDown, loRaDevice.FCntDown);
            Assert.Equal(expectedFcntDown, payloadDataDown.Fcnt);
            Assert.Equal(0U, loRaDevice.FCntDown - loRaDevice.LastSavedFCntDown);

            // 6. Frame count has no pending changes
            Assert.False(loRaDevice.HasFrameCountChanges);
        }
示例#11
0
        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");

            var loRaDeviceClient1 = new Mock <ILoRaDeviceClient>(MockBehavior.Loose);

            loRaDeviceClient1.Setup(x => x.GetTwinAsync(CancellationToken.None))
            .ReturnsAsync(simulatedDevice1.CreateABPTwin());

            using var connectionManager1 = new SingleDeviceConnectionManager(loRaDeviceClient1.Object);
            using var loraDevice1        = TestUtils.CreateFromSimulatedDevice(simulatedDevice1, connectionManager1);
            var devAddr = loraDevice1.DevAddr.Value;

            var reqHandler1 = new Mock <ILoRaDataRequestHandler>(MockBehavior.Strict);

            reqHandler1.Setup(x => x.ProcessRequestAsync(It.IsNotNull <LoRaRequest>(), loraDevice1))
            .ReturnsAsync(new LoRaDeviceRequestProcessResult(loraDevice1, null));
            loraDevice1.SetRequestHandler(reqHandler1.Object);

            var simulatedDevice2 = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID));

            simulatedDevice2.LoRaDevice.DeviceID = new DevEui(2).ToString();
            simulatedDevice2.LoRaDevice.NwkSKey  = TestKeys.CreateNetworkSessionKey(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF);
            var loRaDeviceClient2 = new Mock <ILoRaDeviceClient>(MockBehavior.Loose);

            loRaDeviceClient2.Setup(x => x.GetTwinAsync(CancellationToken.None))
            .ReturnsAsync(simulatedDevice2.CreateABPTwin());
            using var connectionManager2 = new SingleDeviceConnectionManager(loRaDeviceClient2.Object);
            using var loraDevice2        = TestUtils.CreateFromSimulatedDevice(simulatedDevice2, connectionManager2);

            // 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.CreateAndRegisterAsync(iotHubDeviceInfo1, It.IsAny <CancellationToken>())).ReturnsAsync(() => {
                DeviceCache.Register(loraDevice1);
                return(loraDevice1);
            });
            this.loraDeviceFactoryMock.Setup(x => x.CreateAndRegisterAsync(iotHubDeviceInfo2, It.IsAny <CancellationToken>())).ReturnsAsync(() => {
                DeviceCache.Register(loraDevice2);
                return(loraDevice2);
            });

            using var target  = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, this.loraDeviceFactoryMock.Object, DeviceCache);
            using var request = WaitableLoRaRequest.Create(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
            Assert.Equal(2, DeviceCache.RegistrationCount(devAddr)); // 2 devices with same devAddr exist in cache

            // find device 1
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, out var actualCachedLoRaDevice1));
            Assert.Same(loraDevice1, actualCachedLoRaDevice1);
            Assert.True(loraDevice1.IsOurDevice);

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

            reqHandler1.VerifyAll();
        }