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(); }
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); }
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); }
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(); }
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(); }
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); }
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); }
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(); }