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 When_Join_Fails_Due_To_GetTwin_Error_Second_Try_Should_Reload_Device_Twin(string deviceGatewayID) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID)); var joinRequestPayload1 = simulatedDevice.CreateJoinRequest(); 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(); if (deviceGatewayID != null) { twin.Properties.Desired[TwinProperty.GatewayID] = deviceGatewayID; } twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder; LoRaDeviceClient.SetupSequence(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync((Twin)null) .ReturnsAsync(twin); // Device twin will be updated AppSessionKey? afterJoinAppSKey = null; NetworkSessionKey?afterJoinNwkSKey = null; string afterJoinDevAddr = null; LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .Callback <TwinCollection, CancellationToken>((updatedTwin, _) => { afterJoinAppSKey = AppSessionKey.Parse(updatedTwin[TwinProperty.AppSKey].Value); afterJoinNwkSKey = NetworkSessionKey.Parse(updatedTwin[TwinProperty.NwkSKey].Value); afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr]; }) .ReturnsAsync(true); // Lora device api will be search by devices with matching deveui, LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequestPayload1.DevNonce)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList())); // using factory to create mock of using var cache = NewMemoryCache(); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); // 1st join request // Should fail using var joinRequest1 = CreateWaitableRequest(joinRequestPayload1); messageProcessor.DispatchRequest(joinRequest1); Assert.True(await joinRequest1.WaitCompleteAsync()); Assert.True(joinRequest1.ProcessingFailed); Assert.Null(joinRequest1.ResponseDownlink); // 2nd attempt var joinRequestPayload2 = simulatedDevice.CreateJoinRequest(); // will reload the device matched by deveui LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequestPayload2.DevNonce)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList())); using var joinRequest2 = CreateWaitableRequest(joinRequestPayload2); messageProcessor.DispatchRequest(joinRequest2); Assert.True(await joinRequest2.WaitCompleteAsync()); Assert.NotNull(joinRequest2.ResponseDownlink); Assert.True(joinRequest2.ProcessingSucceeded); Assert.Single(DownstreamMessageSender.DownlinkMessages); var joinRequestDownlinkMessage2 = DownstreamMessageSender.DownlinkMessages[0]; var joinAccept = new LoRaPayloadJoinAccept(joinRequestDownlinkMessage2.Data, simulatedDevice.LoRaDevice.AppKey.Value); Assert.Equal(joinAccept.DevAddr.ToString(), afterJoinDevAddr); Assert.True(DeviceCache.TryGetByDevEui(devEui, out var loRaDevice)); Assert.Equal(simulatedDevice.AppKey, loRaDevice.AppKey); Assert.Equal(simulatedDevice.AppEui, loRaDevice.AppEui); Assert.Equal(afterJoinAppSKey, loRaDevice.AppSKey); Assert.Equal(afterJoinNwkSKey, loRaDevice.NwkSKey); Assert.Equal(afterJoinDevAddr, loRaDevice.DevAddr.ToString()); if (deviceGatewayID == null) { Assert.Null(loRaDevice.GatewayID); } else { Assert.Equal(deviceGatewayID, loRaDevice.GatewayID); } // fcnt is restarted Assert.Equal(0U, loRaDevice.FCntUp); Assert.Equal(0U, loRaDevice.FCntDown); Assert.False(loRaDevice.HasFrameCountChanges); // should get twin 2x (1st failed) LoRaDeviceClient.Verify(x => x.GetTwinAsync(CancellationToken.None), Times.Exactly(2)); // should get device for join 2x LoRaDeviceApi.Verify(x => x.SearchAndLockForJoinAsync(ServerGatewayID, devEui, It.IsAny <DevNonce>())); LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }
public async Task Multi_OTAA_Unconfirmed_Message_Should_Send_Data_To_IotHub_Update_FcntUp_And_Return_Null() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1)); // 1 messages will be sent LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); SecondLoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(It.IsNotNull <DevEui>(), It.IsAny <uint>(), It.IsNotNull <string>())) .ReturnsAsync(true); SecondLoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(It.IsNotNull <DevEui>(), It.IsAny <uint>(), It.IsNotNull <string>())) .ReturnsAsync(true); // cloud to device messages will be checked twice LoRaDeviceClient.SetupSequence(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null) .ReturnsAsync((Message)null); SecondLoRaDeviceClient.SetupSequence(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null) .ReturnsAsync((Message)null); using var loRaDevice1 = CreateLoRaDevice(simulatedDevice); using var connectionManager2 = new SingleDeviceConnectionManager(SecondLoRaDeviceClient.Object); using var loRaDevice2 = TestUtils.CreateFromSimulatedDevice(simulatedDevice, connectionManager2, SecondRequestHandlerImplementation); using var cache1 = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loRaDevice1); using var loRaDeviceRegistry1 = new LoRaDeviceRegistry(ServerConfiguration, cache1, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); using var cache2 = EmptyMemoryCache(); using var loraDeviceCache2 = CreateDeviceCache(loRaDevice2); using var loRaDeviceRegistry2 = new LoRaDeviceRegistry(ServerConfiguration, cache2, SecondLoRaDeviceApi.Object, SecondLoRaDeviceFactory, loraDeviceCache2); // Send to message processor using var messageProcessor1 = new MessageDispatcher( ServerConfiguration, loRaDeviceRegistry1, FrameCounterUpdateStrategyProvider); using var messageProcessor2 = new MessageDispatcher( SecondServerConfiguration, loRaDeviceRegistry2, SecondFrameCounterUpdateStrategyProvider); // Starts with fcnt up zero Assert.Equal(0U, loRaDevice1.FCntUp); Assert.Equal(0U, loRaDevice2.FCntUp); var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: 1); // Create Rxpk using var request1 = CreateWaitableRequest(payload, DownstreamMessageSender); using var request2 = CreateWaitableRequest(payload, SecondDownstreamMessageSender); messageProcessor1.DispatchRequest(request1); messageProcessor2.DispatchRequest(request2); await Task.WhenAll(request1.WaitCompleteAsync(), request2.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub LoRaDeviceClient.VerifyAll(); SecondLoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); SecondLoRaDeviceApi.VerifyAll(); LoRaDeviceClient.Verify(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null), Times.Once()); SecondLoRaDeviceClient.Verify(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null), Times.Once()); // 2. Return is null (there is nothing to send downstream) Assert.Null(request1.ResponseDownlink); Assert.Null(request2.ResponseDownlink); // 3. Frame counter up was updated to 1 Assert.Equal(1U, loRaDevice1.FCntUp); Assert.Equal(1U, loRaDevice2.FCntUp); // the following setup is required after VerifyAll() is called LoRaDeviceClient.Setup(ldc => ldc.Dispose()); SecondLoRaDeviceClient.Setup(ldc => ldc.Dispose()); }
public async Task OTAA_Unconfirmed_With_Cloud_To_Device_Mac_Command_Returns_Downstream_Message(string msg) { const uint PayloadFcnt = 20; 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); var c2d = new ReceivedLoRaCloudToDeviceMessage() { Payload = msg, MacCommands = { new DevStatusRequest() }, }; if (!string.IsNullOrEmpty(msg)) { c2d.Fport = TestPort; } using var cloudToDeviceMessage = c2d.CreateMessage(); 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.CompleteAsync(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); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); // 2. Return is downstream message Assert.NotNull(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); Assert.Single(DownstreamMessageSender.DownlinkMessages); var downlinkMessage = DownstreamMessageSender.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData(downlinkMessage.Data); // in case no payload the mac is in the FRMPayload and is decrypted with NwkSKey if (string.IsNullOrEmpty(msg)) { payloadDataDown.Serialize(loraDevice.NwkSKey.Value); } else { 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 Assert.Equal(InitialDeviceFcntDown + 1, loraDevice.FCntDown); Assert.Equal(InitialDeviceFcntDown + 1, payloadDataDown.Fcnt); // 6. Frame count has no pending changes Assert.False(loraDevice.HasFrameCountChanges); // 7. Mac commands should be present in reply // mac command is in fopts if there is a c2d message if (string.IsNullOrEmpty(msg)) { Assert.Equal(FramePort.MacCommand, payloadDataDown.Fport); Assert.NotNull(payloadDataDown.Frmpayload.Span.ToArray()); Assert.Single(payloadDataDown.Frmpayload.Span.ToArray()); Assert.Equal((byte)LoRaTools.Cid.DevStatusCmd, payloadDataDown.Frmpayload.Span[0]); } else { Assert.NotNull(payloadDataDown.Fopts.Span.ToArray()); Assert.Single(payloadDataDown.Fopts.Span.ToArray()); Assert.Equal((byte)LoRaTools.Cid.DevStatusCmd, payloadDataDown.Fopts.Span[0]); } LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }
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 OTAA_Confirmed_With_Cloud_To_Device_Message_Returns_Downstream_Message(uint initialDeviceFcntUp, uint payloadFcnt) { const uint InitialDeviceFcntDown = 20; var needsToSaveFcnt = payloadFcnt - initialDeviceFcntUp >= Constants.MaxFcntUnsavedDelta; 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); if (needsToSaveFcnt) { LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true); } using var cloudToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "c2d", Fport = TestPort } .CreateMessage(); 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.CompleteAsync(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.CreateConfirmedDataUpMessage("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. Return is downstream message Assert.NotNull(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); Assert.Single(DownstreamMessageSender.DownlinkMessages); var downlinkMessage = DownstreamMessageSender.DownlinkMessages[0]; 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 Assert.Equal(InitialDeviceFcntDown + 1, loraDevice.FCntDown); Assert.Equal(InitialDeviceFcntDown + 1, payloadDataDown.Fcnt); // 6. Frame count has pending changes? if (needsToSaveFcnt) { Assert.False(loraDevice.HasFrameCountChanges); } else { Assert.True(loraDevice.HasFrameCountChanges); } }