public async Task After_Sending_Class_C_Downstream_Should_Disconnect_Client() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID, deviceClassType: 'c')); var devEui = simulatedDevice.DevEUI; // will disconnected client using var disconnectedEvent = new SemaphoreSlim(0, 1); LoRaDeviceClient.Setup(x => x.Disconnect()) .Callback(() => { disconnectedEvent.Release(); }) .Returns(true); // will check client connection LoRaDeviceClient.Setup(x => x.EnsureConnected()) .Returns(true); // will save twin LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEui, Fport = FramePorts.App10, MessageId = Guid.NewGuid().ToString(), }; var cachedDevice = CreateLoRaDevice(simulatedDevice); cachedDevice.KeepAliveTimeout = 3; cachedDevice.LoRaRegion = LoRaRegionType.EU868; cachedDevice.InternalAcceptChanges(); cachedDevice.SetFcntDown(cachedDevice.FCntDown + Constants.MaxFcntUnsavedDelta - 1); cachedDevice.SetLastProcessingStationEui(new StationEui(ulong.MaxValue)); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(cachedDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); var target = new DefaultClassCDevicesMessageSender( ServerConfiguration, deviceRegistry, DownstreamMessageSender, FrameCounterUpdateStrategyProvider, new TestOutputLogger <DefaultClassCDevicesMessageSender>(testOutputHelper), TestMeter.Instance); Assert.True(await target.SendAsync(c2dToDeviceMessage)); Assert.Single(DownstreamMessageSender.DownlinkMessages); await EnsureDisconnectedAsync(disconnectedEvent); LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }
public async Task When_No_Decoder_Is_Defined_Sends_Raw_Payload(string deviceGatewayID, string msgPayload, string sensorDecoder) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID)); var loRaDevice = CreateLoRaDevice(simulatedDevice); loRaDevice.SensorDecoder = sensorDecoder; // 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); if (string.IsNullOrEmpty(deviceGatewayID)) { // multi GW will reset LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(It.IsNotNull <DevEui>(), It.IsAny <uint>(), It.IsNotNull <string>())) .ReturnsAsync(true); } using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loRaDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); // Send to message processor using var messageDispatcher = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); // sends unconfirmed message var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage(msgPayload, fcnt: 1); using var request = CreateWaitableRequest(unconfirmedMessagePayload); messageDispatcher.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.Null(request.ResponseDownlink); Assert.NotNull(loRaDeviceTelemetry); Assert.IsType <UndecodedPayload>(loRaDeviceTelemetry.Data); var undecodedPayload = (UndecodedPayload)loRaDeviceTelemetry.Data; var rawPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(msgPayload)); Assert.Equal(rawPayload, loRaDeviceTelemetry.Rawdata); Assert.Equal(rawPayload, undecodedPayload.Value); // Validate json var actualJsonTelemetry = JsonConvert.SerializeObject(loRaDeviceTelemetry, Formatting.None); var expectedTelemetryJson = $"{{\"time\":100000,\"tmms\":100000,\"freq\":868.3,\"chan\":2,\"rfch\":1,\"modu\":\"LoRa\",\"datr\":\"SF10BW125\",\"rssi\":2.0,\"lsnr\":0.1,\"data\":{{\"value\":\"{rawPayload}\"}},\"port\":1,\"fcnt\":1,\"edgets\":{loRaDeviceTelemetry.Edgets},\"rawdata\":\"{rawPayload}\",\"eui\":\"0000000000000001\",\"gatewayid\":\"test-gateway\",\"stationeui\":\"0000000000000000\"}}"; Assert.Equal(expectedTelemetryJson, actualJsonTelemetry); LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }
public async Task When_Api_Returns_DevAlreadyUsed_Should_Return_Null() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: null)); var joinRequest = simulatedDevice.CreateJoinRequest(); var devEui = simulatedDevice.LoRaDevice.DevEui; // Lora device api will be search by devices with matching deveui, LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequest.DevNonce)) .ReturnsAsync(new SearchDevicesResult() { IsDevNonceAlreadyUsed = true }); using var memoryCache = new MemoryCache(new MemoryCacheOptions()); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.Null(request.ResponseDownlink); LoRaDeviceApi.VerifyAll(); LoRaDeviceClient.VerifyAll(); }
public async Task When_Device_Is_Loaded_Should_Disconnect_After_Sending_Data() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerGatewayID)); // will search for the device by devAddr LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simulatedDevice.DevAddr, simulatedDevice.DevEUI, "ada").AsList())); // will read the device twins var twin = simulatedDevice.CreateABPTwin(desiredProperties: new Dictionary <string, object> { { TwinProperty.KeepAliveTimeout, 3 } }); LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(twin); // 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); // will check client connection LoRaDeviceClient.Setup(x => x.EnsureConnected()) .Returns(true); // will disconnected client using var disconnectedEvent = new SemaphoreSlim(0, 1); LoRaDeviceClient.Setup(x => x.Disconnect()) .Callback(() => disconnectedEvent.Release()) .Returns(true); using var cache = NewMemoryCache(); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); using var messageDispatcher = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); // sends unconfirmed message var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("hello"); using var request = CreateWaitableRequest(unconfirmedMessagePayload); messageDispatcher.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); await EnsureDisconnectedAsync(disconnectedEvent, (int)TimeSpan.FromSeconds(Constants.MinKeepAliveTimeout * 2).TotalMilliseconds); LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }
public async Task ABP_From_Another_Gateway_Unconfirmed_Message_Should_Load_Device_Cache_And_Disconnect() { const string gateway1 = "gateway1"; const string gateway2 = "gateway2"; var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: gateway1)) { FrmCntUp = 9 }; LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simulatedDevice.DevAddr, simulatedDevice.DevEUI, "1234") { GatewayId = gateway2 }.AsList())); 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 payload1 = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 10); using var request1 = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload1); 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 Assert.True(DeviceCache.TryGetByDevEui(simulatedDevice.DevEUI, out var cachedDevice)); var payload2 = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 11); using var request2 = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload2); messageProcessor.DispatchRequest(request2); Assert.True(await request2.WaitCompleteAsync()); Assert.True(request2.ProcessingFailed); Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request2.ProcessingFailedReason); // Expectations // 1. we never loaded the twins for the device not belonging to us LoRaDeviceClient.Verify(x => x.GetTwinAsync(It.IsAny <CancellationToken>()), Times.Never); LoRaDeviceClient.Verify(x => x.EnsureConnected(), Times.Never); LoRaDeviceApi.VerifyAll(); LoRaDeviceApi.Verify(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value), Times.Once()); // 2. The device is registered Assert.Equal(1, DeviceCache.RegistrationCount(simulatedDevice.DevAddr.Value)); }
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 OTAA_Unconfirmed_Message_With_Fcnt_Change_Of_10_Should_Send_Data_To_IotHub_Update_FcntUp_And_Return_Null() { const uint PayloadFcnt = 19; const uint InitialDeviceFcntUp = 9; const uint InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = CreateLoRaDevice(simulatedDevice); LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync((Message)null); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loraDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); // 2. Return nothing Assert.Null(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); // 4. Frame counter up was updated Assert.Equal(PayloadFcnt, loraDevice.FCntUp); // 5. Frame counter down is not changed Assert.Equal(InitialDeviceFcntDown, loraDevice.FCntDown); // 6. Frame count has no pending changes Assert.False(loraDevice.HasFrameCountChanges); }
public async Task OTAA_Unconfirmed_With_Cloud_To_Device_Mac_Command_Fails_Due_To_Wrong_Setup(string mac, int?fport) { const int PayloadFcnt = 20; const int InitialDeviceFcntUp = 9; const int InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = CreateLoRaDevice(simulatedDevice); LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true); var c2dJson = $"{{\"fport\":{fport}, \"payload\":\"asd\", \"macCommands\": [ {{ \"cid\": \"{mac}\" }}] }}"; using var cloudToDeviceMessage = new Message(Encoding.UTF8.GetBytes(c2dJson)); LoRaDeviceClient.SetupSequence(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync(cloudToDeviceMessage) .ReturnsAsync((Message)null); // 2nd cloud to device message does not return anything LoRaDeviceClient.Setup(x => x.RejectAsync(cloudToDeviceMessage)) .ReturnsAsync(true); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loraDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt); using var request = CreateWaitableRequest(payload, constantElapsedTime: TimeSpan.Zero); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); // 2. DownStream Message should be null as the processing should fail Assert.Null(request.ResponseDownlink); }
public async Task After_ClassA_Sends_Data_Should_Disconnect() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1)) { FrmCntUp = 10 }; // 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); // will check client connection LoRaDeviceClient.Setup(x => x.EnsureConnected()) .Returns(true); // will disconnected client using var disconnectedEvent = new SemaphoreSlim(0, 1); LoRaDeviceClient.Setup(x => x.Disconnect()) .Callback(() => disconnectedEvent.Release()) .Returns(true); var cachedDevice = CreateLoRaDevice(simulatedDevice); cachedDevice.KeepAliveTimeout = 3; using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(cachedDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); using var messageDispatcher = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); // sends unconfirmed message var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("hello"); using var request = CreateWaitableRequest(unconfirmedMessagePayload); messageDispatcher.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); await EnsureDisconnectedAsync(disconnectedEvent); LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }
public async Task OTAA_Confirmed_Message_Should_Send_Data_To_IotHub_Update_FcntUp_And_Return_DownstreamMessage() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID)); var payload = simulatedDevice.CreateConfirmedDataUpMessage("1234"); var loraDevice = CreateLoRaDevice(simulatedDevice); LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loraDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), 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.NotNull(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); Assert.Single(DownstreamMessageSender.DownlinkMessages); var downlinkMessage = DownstreamMessageSender.DownlinkMessages.First(); var payloadDataDown = new LoRaPayloadData(downlinkMessage.Data); Assert.Equal(payloadDataDown.DevAddr, loraDevice.DevAddr); Assert.False(payloadDataDown.IsConfirmed); Assert.Equal(MacMessageType.UnconfirmedDataDown, payloadDataDown.MessageType); // 4. Frame counter up was updated Assert.Equal(1U, loraDevice.FCntUp); // 5. Frame counter down was incremented Assert.Equal(1U, loraDevice.FCntDown); Assert.Equal(1, payloadDataDown.Fcnt); }
public async Task OTAA_Join_Should_Use_Rchf_0(string deviceGatewayID, uint rfch) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID)); var joinRequestPayload = simulatedDevice.CreateJoinRequest(); var devAddr = (DevAddr?)null; var devEui = simulatedDevice.LoRaDevice.DevEui; // Device twin will be queried var twin = new Twin(); twin.Properties.Desired[TwinProperty.DevEUI] = devEui.ToString(); twin.Properties.Desired[TwinProperty.AppEui] = simulatedDevice.LoRaDevice.AppEui?.ToString(); twin.Properties.Desired[TwinProperty.AppKey] = simulatedDevice.LoRaDevice.AppKey?.ToString(); twin.Properties.Desired[TwinProperty.GatewayID] = deviceGatewayID; twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder; LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(twin); // Device twin will be updated LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true); // Lora device api will be search by devices with matching deveui, LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequestPayload.DevNonce)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList())); // using factory to create mock of using var memoryCache = new MemoryCache(new MemoryCacheOptions()); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); var radio = TestUtils.GenerateTestRadioMetadata(antennaPreference: rfch); using var joinRequest = CreateWaitableRequest(radio, joinRequestPayload); messageProcessor.DispatchRequest(joinRequest); Assert.True(await joinRequest.WaitCompleteAsync()); Assert.True(joinRequest.ProcessingSucceeded); Assert.NotNull(joinRequest.ResponseDownlink); Assert.Single(DownstreamMessageSender.DownlinkMessages); var downlinkJoinAcceptMessage = DownstreamMessageSender.DownlinkMessages[0]; // validates txpk according to eu region Assert.True(RegionManager.EU868.TryGetDownstreamChannelFrequency(radio.Frequency, joinRequest.RadioMetadata.DataRate, deviceJoinInfo: null, downstreamFrequency: out var receivedFrequency)); Assert.Equal(receivedFrequency, downlinkJoinAcceptMessage.Rx1?.Frequency); LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }
public async Task Validate_Function_Bundler_Execution() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1)); var devEUI = simulatedDevice.LoRaDevice.DevEui; var loRaDevice = CreateLoRaDevice(simulatedDevice); loRaDevice.Deduplication = DeduplicationMode.Drop; LoRaDeviceApi .Setup(x => x.ExecuteFunctionBundlerAsync(devEUI, It.IsAny <FunctionBundlerRequest>())) .ReturnsAsync(() => new FunctionBundlerResult { AdrResult = new LoRaTools.ADR.LoRaADRResult { CanConfirmToDevice = true, FCntDown = simulatedDevice.FrmCntDown + 1, }, DeduplicationResult = new DeduplicationResult(), NextFCntDown = simulatedDevice.FrmCntDown + 1 }); LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(It.IsNotNull <DevEui>(), It.IsAny <uint>(), It.IsNotNull <string>())) .ReturnsAsync(true); LoRaDeviceClient .Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceClient .Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync((Message)null); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loRaDevice); using var loRaDeviceRegistry1 = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); using var messageProcessor1 = new MessageDispatcher( ServerConfiguration, loRaDeviceRegistry1, FrameCounterUpdateStrategyProvider); var payload = simulatedDevice.CreateConfirmedDataUpMessage("1234", fcnt: 1); using var request = CreateWaitableRequest(payload); messageProcessor1.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); 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_ABP_Device_With_Relaxed_FrameCounter_Has_FCntUP_Zero_Or_One_Should_Reset_Counter_And_Process_Message(uint payloadFCnt) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID)); // generate payload with frame count 0 or 1 var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFCnt); simulatedDevice.FrmCntDown = 0; simulatedDevice.FrmCntUp = 10; var loraDevice = CreateLoRaDevice(simulatedDevice); // Will send the event to IoT Hub LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); // will try to get C2D message LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())).ReturnsAsync((Message)null); // Will save the fcnt up/down to zero LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.Is <TwinCollection>((t) => IsTwinFcntZero(t)), It.IsAny <CancellationToken>())) .ReturnsAsync(true); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loraDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); // 3. Return is null (there is nothing to send downstream) Assert.Null(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); // 4. Frame counter up was updated Assert.Equal(payloadFCnt, loraDevice.FCntUp); }
public async Task When_Getting_DLSettings_From_Twin_Returns_JoinAccept_With_Correct_Settings(int rx1DROffset, DataRateIndex rx2datarate) { var deviceGatewayID = ServerGatewayID; var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID)); var joinRequest = simulatedDevice.CreateJoinRequest(); var devAddr = (DevAddr?)null; var devEui = simulatedDevice.LoRaDevice.DevEui; // Device twin will be queried var twin = new Twin(); twin.Properties.Desired[TwinProperty.DevEUI] = devEui.ToString(); twin.Properties.Desired[TwinProperty.AppEui] = simulatedDevice.LoRaDevice.AppEui?.ToString(); twin.Properties.Desired[TwinProperty.AppKey] = simulatedDevice.LoRaDevice.AppKey?.ToString(); twin.Properties.Desired[TwinProperty.GatewayID] = deviceGatewayID; twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder; twin.Properties.Desired[TwinProperty.RX1DROffset] = rx1DROffset; twin.Properties.Desired[TwinProperty.RX2DataRate] = rx2datarate; LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(twin); LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true); // Lora device api will be search by devices with matching deveui, LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequest.DevNonce)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").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 messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.NotNull(request.ResponseDownlink); Assert.Single(DownstreamMessageSender.DownlinkMessages); var downlinkMessage = DownstreamMessageSender.DownlinkMessages[0]; var joinAccept = new LoRaPayloadJoinAccept(downlinkMessage.Data, simulatedDevice.LoRaDevice.AppKey.Value); Assert.Equal(rx1DROffset, joinAccept.Rx1DrOffset); Assert.Equal(rx2datarate, joinAccept.Rx2Dr); }
public async Task When_Device_With_Downlink_Disabled_Received_Unconfirmed_Data_Should_Not_Check_For_C2D(string deviceGatewayID) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID)); // message will be sent LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); if (string.IsNullOrEmpty(deviceGatewayID)) { LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(It.IsNotNull <DevEui>(), It.IsAny <uint>(), It.IsNotNull <string>())) .ReturnsAsync(true); } using var memoryCache = new MemoryCache(new MemoryCacheOptions()); var cachedDevice = CreateLoRaDevice(simulatedDevice); cachedDevice.DownlinkEnabled = false; DeviceCache.Register(cachedDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); // sends unconfirmed message var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234"); using var request = CreateWaitableRequest(unconfirmedMessagePayload); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.Null(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); LoRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsAny <TimeSpan>()), Times.Never()); LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }
public async Task When_Device_AppEUI_Does_Not_Match_Should_Return_Null() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: ServerConfiguration.GatewayID)); var joinRequest = simulatedDevice.CreateJoinRequest(); var devEui = simulatedDevice.LoRaDevice.DevEui; simulatedDevice.LoRaDevice.AppEui = new JoinEui(0xFFFFFFFFFFFFFFFF); using var connectionManager = new SingleDeviceConnectionManager(LoRaDeviceClient.Object); using var loRaDevice = TestUtils.CreateFromSimulatedDevice(simulatedDevice, connectionManager); loRaDevice.SetFcntDown(10); loRaDevice.SetFcntUp(20); var loRaDeviceRegistryMock = new Mock <ILoRaDeviceRegistry>(MockBehavior.Strict); loRaDeviceRegistryMock.Setup(x => x.RegisterDeviceInitializer(It.IsNotNull <ILoRaDeviceInitializer>())); loRaDeviceRegistryMock.Setup(x => x.GetDeviceForJoinRequestAsync(devEui, joinRequest.DevNonce)) .ReturnsAsync(() => loRaDevice); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, loRaDeviceRegistryMock.Object, FrameCounterUpdateStrategyProvider); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.Null(request.ResponseDownlink); // Device frame counts did not changed Assert.Equal(10U, loRaDevice.FCntDown); Assert.Equal(20U, loRaDevice.FCntUp); LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); loRaDeviceRegistryMock.VerifyAll(); loRaDeviceRegistryMock.Setup(dr => dr.Dispose()); LoRaDeviceClient.Setup(ldc => ldc.Dispose()); }
public async Task When_Mic_Check_Fails_Join_Process_Should_Fail() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: ServerConfiguration.GatewayID)); var wrongAppKey = TestKeys.CreateAppKey(0x3000000, 0x30000); var joinRequest = simulatedDevice.CreateJoinRequest(wrongAppKey); using var loRaDevice = CreateLoRaDevice(simulatedDevice); loRaDevice.SetFcntDown(10); loRaDevice.SetFcntUp(20); var devEui = simulatedDevice.LoRaDevice.DevEui; var loRaDeviceRegistryMock = new Mock <ILoRaDeviceRegistry>(MockBehavior.Strict); loRaDeviceRegistryMock.Setup(x => x.RegisterDeviceInitializer(It.IsNotNull <ILoRaDeviceInitializer>())); loRaDeviceRegistryMock.Setup(x => x.GetDeviceForJoinRequestAsync(devEui, joinRequest.DevNonce)) .ReturnsAsync(() => loRaDevice); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, loRaDeviceRegistryMock.Object, FrameCounterUpdateStrategyProvider); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.Null(request.ResponseDownlink); // Device frame counts were not modified Assert.Equal(10U, loRaDevice.FCntDown); Assert.Equal(20U, loRaDevice.FCntUp); // Twin property were updated LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); loRaDeviceRegistryMock.VerifyAll(); loRaDeviceRegistryMock.Setup(dr => dr.Dispose()); LoRaDeviceClient.Setup(ldc => ldc.Dispose()); }
public async Task Join_Device_Has_Mismatching_AppKey_Should_Return_Null(string deviceGatewayID) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID)); var joinRequestPayload = simulatedDevice.CreateJoinRequest(); var devAddr = (DevAddr?)null; var devEui = simulatedDevice.LoRaDevice.DevEui; // Device twin will be queried var twin = new Twin(); twin.Properties.Desired[TwinProperty.DevEUI] = devEui.ToString(); twin.Properties.Desired[TwinProperty.AppEui] = simulatedDevice.LoRaDevice.AppEui?.ToString(); twin.Properties.Desired[TwinProperty.AppKey] = "012345678901234567890123456789FF"; twin.Properties.Desired[TwinProperty.GatewayID] = deviceGatewayID; twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder; LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(twin); // Lora device api will be search by devices with matching deveui, LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequestPayload.DevNonce)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList())); // using factory to create mock of using var memoryCache = new MemoryCache(new MemoryCacheOptions()); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); // join request should fail using var joinRequest = CreateWaitableRequest(joinRequestPayload); messageProcessor.DispatchRequest(joinRequest); Assert.True(await joinRequest.WaitCompleteAsync()); Assert.True(joinRequest.ProcessingFailed); Assert.Null(joinRequest.ResponseDownlink); LoRaDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Never); LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }
public async Task When_Api_Takes_Too_Long_Should_Return_Null() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: ServerConfiguration.GatewayID)); var joinRequest = simulatedDevice.CreateJoinRequest(); using var loRaDevice = CreateLoRaDevice(simulatedDevice); loRaDevice.SetFcntDown(10); loRaDevice.SetFcntUp(20); var devEui = simulatedDevice.LoRaDevice.DevEui; var loRaDeviceRegistryMock = new Mock <ILoRaDeviceRegistry>(MockBehavior.Loose); loRaDeviceRegistryMock.Setup(x => x.RegisterDeviceInitializer(It.IsNotNull <ILoRaDeviceInitializer>())); loRaDeviceRegistryMock.Setup(x => x.GetDeviceForJoinRequestAsync(devEui, joinRequest.DevNonce)) .ReturnsAsync(loRaDevice); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, loRaDeviceRegistryMock.Object, FrameCounterUpdateStrategyProvider); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest, constantElapsedTime: TimeSpan.FromSeconds(7)); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.Null(request.ResponseDownlink); Assert.Empty(DownstreamMessageSender.DownlinkMessages); Assert.Equal(LoRaDeviceRequestFailedReason.ReceiveWindowMissed, request.ProcessingFailedReason); // Device frame counts were not modified Assert.Equal(10U, loRaDevice.FCntDown); Assert.Equal(20U, loRaDevice.FCntUp); // Twin property were updated LoRaDeviceClient.VerifyAll(); loRaDeviceRegistryMock.VerifyAll(); LoRaDeviceApi.VerifyAll(); LoRaDeviceClient.Setup(ldc => ldc.Dispose()); }
public async Task ABP_Unconfirmed_Message_Should_Send_Data_To_IotHub_Update_FcntUp_And_Return_Null() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID)); var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: 10); simulatedDevice.FrmCntUp = 9; var loraDevice = CreateLoRaDevice(simulatedDevice); 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 = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loraDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); Assert.True(request.ProcessingSucceeded); // 2. Return is null (there is nothing to send downstream) Assert.Null(request.ResponseDownlink); // 3. Frame counter up was updated Assert.Equal(10U, loraDevice.FCntUp); }
public DwellTimeIntegrationTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { this.testOutputLoggerFactory = new TestOutputLoggerFactory(testOutputHelper); this.dataRequestHandlerMock = new Mock <TestDefaultLoRaRequestHandler>(MockBehavior.Default, ServerConfiguration, FrameCounterUpdateStrategyProvider, ConcentratorDeduplication, PayloadDecoder, new DeduplicationStrategyFactory(this.testOutputLoggerFactory, this.testOutputLoggerFactory.CreateLogger <DeduplicationStrategyFactory>()), new LoRaADRStrategyProvider(this.testOutputLoggerFactory), new LoRAADRManagerFactory(LoRaDeviceApi.Object, this.testOutputLoggerFactory), new FunctionBundlerProvider(LoRaDeviceApi.Object, this.testOutputLoggerFactory, this.testOutputLoggerFactory.CreateLogger <FunctionBundlerProvider>()), testOutputHelper) { CallBase = true }; this.simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(0)); this.loRaDevice = CreateLoRaDevice(this.simulatedDevice, registerConnection: false); LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(It.IsAny <DevEui>(), It.IsAny <uint>(), It.IsNotNull <string>())) .ReturnsAsync(true); }
public async Task Unknown_Device_Should_Not_Send_Messages(int searchDevicesDelayMs) { // Setup var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1)); var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234"); if (searchDevicesDelayMs > 0) { LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value)) .Returns(Task.Delay(searchDevicesDelayMs).ContinueWith((_) => new SearchDevicesResult(), TaskScheduler.Default)); } else { LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value)) .ReturnsAsync(new SearchDevicesResult()); } 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); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Returns null Assert.Null(request.ResponseDownlink); Assert.True(request.ProcessingFailed); Assert.Equal(LoRaDeviceRequestFailedReason.NotMatchingDeviceByDevAddr, request.ProcessingFailedReason); }
public async Task ValidateFcnt_Start_Values_And_ResetCounter( short fcntUp, uint startFcntUpDesired, uint startFcntDownDesired, uint?startFcntUpReported, uint?startFcntDownReported, int?fcntResetCounterDesired, int?fcntResetCounterReported, uint startUpExpected, uint startDownExpected, bool saveExpected) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID)); var devEui = simulatedDevice.LoRaDevice.DevEui; var devAddr = simulatedDevice.LoRaDevice.DevAddr.Value; LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)).ReturnsAsync(true); var initialTwin = SetupTwins((uint)fcntUp, startFcntDownDesired, startFcntUpDesired, startFcntDownDesired, true, false, simulatedDevice, devEui, devAddr); if (startFcntUpReported.HasValue) { initialTwin.Properties.Reported[TwinProperty.FCntUpStart] = startFcntUpReported.Value; } if (startFcntDownReported.HasValue) { initialTwin.Properties.Reported[TwinProperty.FCntDownStart] = startFcntDownReported.Value; } if (fcntResetCounterDesired.HasValue) { initialTwin.Properties.Desired[TwinProperty.FCntResetCounter] = fcntResetCounterDesired.Value; } if (fcntResetCounterReported.HasValue) { initialTwin.Properties.Reported[TwinProperty.FCntResetCounter] = fcntResetCounterReported.Value; } LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(initialTwin); uint?fcntUpSavedInTwin = null; uint?fcntDownSavedInTwin = null; uint?fcntStartUpSavedInTwin = null; uint?fcntStartDownSavedInTwin = null; LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .Returns <TwinCollection, CancellationToken>((t, _) => { fcntUpSavedInTwin = (uint)t[TwinProperty.FCntUp]; fcntDownSavedInTwin = (uint)t[TwinProperty.FCntDown]; fcntStartUpSavedInTwin = (uint)t[TwinProperty.FCntUpStart]; fcntStartDownSavedInTwin = (uint)t[TwinProperty.FCntDownStart]; return(Task.FromResult(true)); }); LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())).ReturnsAsync((Message)null); 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); var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: (uint)fcntUp); using var req = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload); messageDispatcher.DispatchRequest(req); await req.WaitCompleteAsync(); if (saveExpected) { Assert.True(req.ProcessingSucceeded); LoRaDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Exactly(1)); Assert.Equal(fcntUpSavedInTwin, fcntStartUpSavedInTwin); Assert.Equal(fcntDownSavedInTwin, fcntStartDownSavedInTwin); Assert.Equal(startUpExpected, fcntStartUpSavedInTwin.Value); Assert.Equal(startDownExpected, fcntStartDownSavedInTwin.Value); } else { LoRaDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Never()); } }
public async Task Validate_Limits( uint payloadFcntUp, uint?deviceFcntUp, uint?deviceFcntDown, uint?startFcntUp, uint?startFcntDown, uint expectedFcntUp, uint expectedFcntDown, bool abpRelaxed, bool confirmed, bool supports32Bit = false, LoRaDeviceRequestFailedReason?failedReason = null) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID, supports32BitFcnt: supports32Bit)); var devEui = simulatedDevice.LoRaDevice.DevEui; var devAddr = simulatedDevice.LoRaDevice.DevAddr.Value; LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)).ReturnsAsync(true); var initialTwin = SetupTwins(deviceFcntUp, deviceFcntDown, startFcntUp, startFcntDown, abpRelaxed, supports32Bit, simulatedDevice, devEui, devAddr); LoRaDeviceClient .Setup(x => x.GetTwinAsync(CancellationToken.None)).Returns(() => { return(Task.FromResult(initialTwin)); }); uint?fcntUpSavedInTwin = null; uint?fcntDownSavedInTwin = null; LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .Returns <TwinCollection, CancellationToken>((t, _) => { fcntUpSavedInTwin = (uint)t[TwinProperty.FCntUp]; fcntDownSavedInTwin = (uint)t[TwinProperty.FCntDown]; return(Task.FromResult(true)); }); LoRaDeviceClient .Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync((Message)null); var shouldReset = payloadFcntUp == 0 && abpRelaxed; if (shouldReset) { LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(devEui, It.IsAny <uint>(), It.IsNotNull <string>())).ReturnsAsync(true); } 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); var loRaPayloadData = confirmed ? simulatedDevice.CreateConfirmedDataUpMessage("1234", fcnt: payloadFcntUp, appSKey: simulatedDevice.AppSKey, nwkSKey: simulatedDevice.NwkSKey) : simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcntUp); using var req = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), loRaPayloadData); messageDispatcher.DispatchRequest(req); Assert.True(await req.WaitCompleteAsync(-1)); if (failedReason.HasValue) { Assert.Equal(failedReason.Value, req.ProcessingFailedReason); } else { Assert.True(req.ProcessingSucceeded); Assert.True(DeviceCache.TryGetByDevEui(devEui, out var loRaDevice)); if (confirmed) { Assert.NotNull(req.ResponseDownlink); Assert.True(req.ProcessingSucceeded); Assert.Single(DownstreamMessageSender.DownlinkMessages); var downlinkMessage = DownstreamMessageSender.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData(downlinkMessage.Data); payloadDataDown.Serialize(simulatedDevice.AppSKey.Value); Assert.Equal(expectedFcntDown, payloadDataDown.Fcnt); } Assert.Equal(expectedFcntUp, loRaDevice.FCntUp); } }
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 When_Updating_PreferredGateway_And_FcntUp_Should_Save_Twin_Once() { const uint PayloadFcnt = 10; const uint InitialDeviceFcntUp = 9; const uint InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, deviceClassType: 'c'), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = CreateLoRaDevice(simulatedDevice); loraDevice.UpdatePreferredGatewayID("another-gateway", acceptChanges: true); var bundlerResult = new FunctionBundlerResult() { PreferredGatewayResult = new PreferredGatewayResult() { DevEUI = simulatedDevice.DevEUI, PreferredGatewayID = ServerGatewayID, CurrentFcntUp = PayloadFcnt, RequestFcntUp = PayloadFcnt, } }; LoRaDeviceApi .Setup(x => x.ExecuteFunctionBundlerAsync(simulatedDevice.DevEUI, It.Is((FunctionBundlerRequest r) => PayloadFcnt == r.ClientFCntUp && ServerGatewayID == r.GatewayId && FunctionBundlerItemType.PreferredGateway == r.FunctionItems))) .ReturnsAsync(bundlerResult); TwinCollection actualSavedTwin = null; LoRaDeviceClient .Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .Callback <TwinCollection, CancellationToken>((savedTwin, _) => actualSavedTwin = savedTwin) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync((Message)null); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loraDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, 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(); // 2. No downstream message for the current device is sent Assert.Null(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); Assert.Equal(ServerGatewayID, loraDevice.PreferredGatewayID); Assert.Equal(LoRaRegionType.EU868, loraDevice.LoRaRegion); Assert.Equal(PayloadFcnt, loraDevice.FCntUp); LoRaDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Once()); Assert.Equal(ServerGatewayID, actualSavedTwin[TwinProperty.PreferredGatewayID].Value as string); Assert.Equal(LoRaRegionType.EU868.ToString(), actualSavedTwin[TwinProperty.Region].Value as string); Assert.Equal(PayloadFcnt, (uint)actualSavedTwin[TwinProperty.FCntUp].Value); }
public async Task When_Processing_Data_Request_Should_Compute_Preferred_Gateway_And_Region( [CombinatorialValues(null, ServerGatewayID)] string deviceGatewayID, [CombinatorialValues(null, ServerGatewayID, "another-gateway")] string initialPreferredGatewayID, [CombinatorialValues(ServerGatewayID, "another-gateway")] string preferredGatewayID, [CombinatorialValues(null, LoRaRegionType.EU868, LoRaRegionType.US915)] LoRaRegionType?initialLoRaRegion) { const uint PayloadFcnt = 10; const uint InitialDeviceFcntUp = 9; const uint InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID, deviceClassType: 'c'), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = CreateLoRaDevice(simulatedDevice); loraDevice.UpdatePreferredGatewayID(initialPreferredGatewayID, acceptChanges: true); if (initialLoRaRegion.HasValue) { loraDevice.UpdateRegion(initialLoRaRegion.Value, acceptChanges: true); } var shouldSavePreferredGateway = string.IsNullOrEmpty(deviceGatewayID) && initialPreferredGatewayID != preferredGatewayID && preferredGatewayID == ServerGatewayID; var shouldSaveRegion = (!initialLoRaRegion.HasValue || initialLoRaRegion.Value != LoRaRegionType.EU868) && (preferredGatewayID == ServerGatewayID || deviceGatewayID != null); var bundlerResult = new FunctionBundlerResult() { PreferredGatewayResult = new PreferredGatewayResult() { DevEUI = simulatedDevice.DevEUI, PreferredGatewayID = preferredGatewayID, CurrentFcntUp = PayloadFcnt, RequestFcntUp = PayloadFcnt, } }; if (string.IsNullOrEmpty(deviceGatewayID)) { LoRaDeviceApi.Setup(x => x.ExecuteFunctionBundlerAsync(simulatedDevice.DevEUI, It.Is((FunctionBundlerRequest r) => r.ClientFCntUp == PayloadFcnt && r.GatewayId == ServerGatewayID && r.FunctionItems == FunctionBundlerItemType.PreferredGateway))) .ReturnsAsync(bundlerResult); } TwinCollection actualSavedTwin = null; if (shouldSavePreferredGateway || shouldSaveRegion) { LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true) .Callback((TwinCollection t, CancellationToken _) => actualSavedTwin = t); } LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync((Message)null); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loraDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt); using var request = CreateWaitableRequest(payload, constantElapsedTime: TimeSpan.Zero); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); // 2. No downstream message for the current device is sent Assert.Null(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); if (!string.IsNullOrEmpty(deviceGatewayID)) { Assert.Equal(initialPreferredGatewayID, loraDevice.PreferredGatewayID); } else { Assert.Equal(preferredGatewayID, loraDevice.PreferredGatewayID); } Assert.Equal(LoRaRegionType.EU868, loraDevice.LoRaRegion); if (shouldSavePreferredGateway || shouldSaveRegion) { if (shouldSavePreferredGateway) { Assert.Equal(ServerGatewayID, actualSavedTwin[TwinProperty.PreferredGatewayID].Value as string); } else { Assert.False(actualSavedTwin.Contains(TwinProperty.PreferredGatewayID)); } if (shouldSaveRegion) { Assert.Equal(LoRaRegionType.EU868.ToString(), actualSavedTwin[TwinProperty.Region].Value as string); } else { Assert.False(actualSavedTwin.Contains(TwinProperty.Region)); } } }
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(); } LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .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; TwinCollection actualTwinCollection = null; LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true) .Callback <TwinCollection, CancellationToken>((t, _) => { savedAppSKey = t[TwinProperty.AppSKey]; savedNwkSKey = t[TwinProperty.NwkSKey]; savedDevAddr = t[TwinProperty.DevAddr]; actualTwinCollection = t; }); LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, simDevice.DevEUI, It.IsAny <DevNonce>())) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simDevice.DevAddr, simDevice.DevEUI, "123").AsList())); using var cache = NewMemoryCache(); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); using var messageDispatcher = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); var joinPayload = simDevice.CreateJoinRequest(); using var joinRequest = CreateWaitableRequest(joinPayload); messageDispatcher.DispatchRequest(joinRequest); Assert.True(await joinRequest.WaitCompleteAsync()); Assert.True(joinRequest.ProcessingSucceeded); Assert.NotEmpty(savedAppSKey); Assert.NotEmpty(savedNwkSKey); Assert.NotEmpty(savedDevAddr); if (shouldSaveRegion) { Assert.Equal(LoRaRegionType.EU868.ToString(), actualTwinCollection[TwinProperty.Region].Value as string); } else { Assert.False(actualTwinCollection.Contains(TwinProperty.Region)); } // Only save preferred gateway if device does not have one assigned if (shouldSavePreferredGateway) { Assert.Equal(ServerConfiguration.GatewayID, actualTwinCollection[TwinProperty.PreferredGatewayID].Value as string); } else { Assert.False(actualTwinCollection.Contains(TwinProperty.PreferredGatewayID)); } Assert.True(DeviceCache.TryGetByDevEui(simDevice.DevEUI, out var loRaDevice)); Assert.Equal(LoRaDeviceClassType.C, loRaDevice.ClassType); if (string.IsNullOrEmpty(simDevice.LoRaDevice.GatewayID)) { Assert.Equal(ServerConfiguration.GatewayID, loRaDevice.PreferredGatewayID); } else { Assert.Empty(loRaDevice.PreferredGatewayID); } Assert.Equal(LoRaRegionType.EU868, loRaDevice.LoRaRegion); LoRaDeviceApi.VerifyAll(); LoRaDeviceClient.VerifyAll(); }
public async Task Unconfirmed_Cloud_To_Device_From_Decoder_Should_Call_ClassC_Message_Sender() { const uint PayloadFcnt = 10; const uint InitialDeviceFcntUp = 9; const uint InitialDeviceFcntDown = 20; var devEui = new DevEui(2); var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = CreateLoRaDevice(simulatedDevice); LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync((Message)null); var decoderResult = new DecodePayloadResult("1") { CloudToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Fport = FramePorts.App1, MessageId = "123", Payload = "12", DevEUI = devEui, }, }; var payloadDecoder = new Mock <ILoRaPayloadDecoder>(MockBehavior.Strict); payloadDecoder.Setup(x => x.DecodeMessageAsync(simulatedDevice.DevEUI, It.IsNotNull <byte[]>(), FramePorts.App1, It.IsAny <string>())) .ReturnsAsync(decoderResult); PayloadDecoder.SetDecoder(payloadDecoder.Object); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loraDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); using var c2dMessageSent = new SemaphoreSlim(0); var classCMessageSender = new Mock <IClassCDeviceMessageSender>(MockBehavior.Strict); classCMessageSender.Setup(x => x.SendAsync(It.Is <IReceivedLoRaCloudToDeviceMessage>(m => !m.Confirmed && m.DevEUI == devEui), It.IsAny <CancellationToken>())) .ReturnsAsync(true) .Callback <IReceivedLoRaCloudToDeviceMessage, CancellationToken>((m, _) => c2dMessageSent.Release()); RequestHandlerImplementation.SetClassCMessageSender(classCMessageSender.Object); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, 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(); // 2. No downstream message for the current device is sent Assert.Null(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); // 4. Frame counter up was updated Assert.Equal(PayloadFcnt, loraDevice.FCntUp); // 5. Frame counter down is unchanged Assert.Equal(InitialDeviceFcntDown, loraDevice.FCntDown); // 6. Frame count has pending changes Assert.True(loraDevice.HasFrameCountChanges); // Ensure the message was sent Assert.True(await c2dMessageSent.WaitAsync(10 * 1000)); payloadDecoder.VerifyAll(); classCMessageSender.VerifyAll(); }