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(); }
internal async Task <MethodResponse> SendCloudToDeviceMessageAsync(MethodRequest methodRequest) { if (!string.IsNullOrEmpty(methodRequest.DataAsJson)) { ReceivedLoRaCloudToDeviceMessage c2d = null; try { c2d = JsonSerializer.Deserialize <ReceivedLoRaCloudToDeviceMessage>(methodRequest.DataAsJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } catch (JsonException ex) { this.logger.LogError($"Impossible to parse Json for c2d message for device {c2d?.DevEUI}, error: {ex}"); return(new MethodResponse((int)HttpStatusCode.BadRequest)); } using var scope = this.logger.BeginDeviceScope(c2d.DevEUI); this.logger.LogDebug($"received cloud to device message from direct method: {methodRequest.DataAsJson}"); using var cts = methodRequest.ResponseTimeout.HasValue ? new CancellationTokenSource(methodRequest.ResponseTimeout.Value) : null; if (await this.classCMessageSender.SendAsync(c2d, cts?.Token ?? CancellationToken.None)) { return(new MethodResponse((int)HttpStatusCode.OK)); } } return(new MethodResponse((int)HttpStatusCode.BadRequest)); }
public async Task When_Device_Is_Not_Joined_Should_Fail() { var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: ServerGatewayID)); var devEUI = simDevice.DevEUI; this.deviceApi.Setup(x => x.SearchByDevEUIAsync(devEUI)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(string.Empty, devEUI, "123").AsList())); this.deviceClient.Setup(x => x.GetTwinAsync()) .ReturnsAsync(simDevice.CreateOTAATwin()); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = 10, MessageId = Guid.NewGuid().ToString(), }; var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.packetForwarder.Object, this.frameCounterStrategyProvider); Assert.False(await target.SendAsync(c2dToDeviceMessage)); this.packetForwarder.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task When_Regions_Is_Not_Defined_Should_Fail() { var simDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, deviceClassType: 'c', gatewayID: ServerGatewayID)); var devEUI = simDevice.DevEUI; var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = LoRaFPort.MacCommand, MessageId = Guid.NewGuid().ToString(), }; var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.packetForwarder.Object, this.frameCounterStrategyProvider); Assert.False(await target.SendAsync(c2dToDeviceMessage)); this.packetForwarder.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task When_Device_Is_Not_Joined_Should_Fail() { var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: ServerGatewayID)); var devEUI = simDevice.DevEUI; this.deviceApi.Setup(x => x.GetPrimaryKeyByEuiAsync(devEUI)) .ReturnsAsync("123"); this.deviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(simDevice.CreateOTAATwin()); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = TestPort, MessageId = Guid.NewGuid().ToString(), }; var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.downstreamMessageSender.Object, this.frameCounterStrategyProvider, NullLogger <DefaultClassCDevicesMessageSender> .Instance, TestMeter.Instance); Assert.False(await target.SendAsync(c2dToDeviceMessage)); this.downstreamMessageSender.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task MessageProcessor_End2End_NoDep_ClassC_CloudToDeviceMessage_SizeLimit_Should_Reject( bool hasMacInC2D) { var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice( 1, deviceClassType: 'c', gatewayID: this.serverConfiguration.GatewayID)); var devEUI = simulatedDevice.DevEUI; this.deviceApi.Setup(x => x.SearchByDevEUIAsync(devEUI)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(string.Empty, devEUI, "123").AsList())); this.deviceClient.Setup(x => x.GetTwinAsync()) .ReturnsAsync(simulatedDevice.CreateABPTwin()); var c2dMessageMacCommand = new DevStatusRequest(); var c2dMessageMacCommandSize = hasMacInC2D ? c2dMessageMacCommand.Length : 0; var datr = this.loRaRegion.DRtoConfiguration[this.loRaRegion.RX2DefaultReceiveWindows.dr].configuration; var c2dPayloadSize = this.loRaRegion.GetMaxPayloadSize(datr) - c2dMessageMacCommandSize + 1 // make message too long on purpose - Constants.LORA_PROTOCOL_OVERHEAD_SIZE; var c2dMsgPayload = this.GeneratePayload("123457890", (int)c2dPayloadSize); var c2d = new ReceivedLoRaCloudToDeviceMessage() { DevEUI = devEUI, Payload = c2dMsgPayload, Fport = 1, }; if (hasMacInC2D) { c2d.MacCommands = new[] { c2dMessageMacCommand }; } var cloudToDeviceMessage = c2d.CreateMessage(); var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.PacketForwarder, this.frameCounterStrategyProvider); // Expectations // Verify that C2D message is sent Assert.False(await target.SendAsync(c2d)); // Verify that exactly one C2D message was received Assert.Empty(this.PacketForwarder.DownlinkMessages); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task When_Sending_Message_Should_Send_Downlink_To_DownstreamMessageSender(string deviceGatewayID) { var simDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, deviceClassType: 'c', gatewayID: deviceGatewayID)); var devEUI = simDevice.DevEUI; this.deviceApi.Setup(x => x.GetPrimaryKeyByEuiAsync(devEUI)) .ReturnsAsync("123"); var twin = simDevice.CreateABPTwin(reportedProperties: new Dictionary <string, object> { { TwinProperty.Region, LoRaRegionType.EU868.ToString() }, { TwinProperty.LastProcessingStationEui, new StationEui(ulong.MaxValue).ToString() } }); this.deviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(twin); if (string.IsNullOrEmpty(deviceGatewayID)) { // will update the fcnt down this.deviceApi.Setup(x => x.NextFCntDownAsync(devEUI, simDevice.FrmCntDown, 0, this.serverConfiguration.GatewayID)) .ReturnsAsync((ushort)(simDevice.FrmCntDown + 1)); } var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = TestPort, MessageId = Guid.NewGuid().ToString(), }; DownlinkMessage receivedDownlinkMessage = null; this.downstreamMessageSender.Setup(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkMessage>())) .Returns(Task.CompletedTask) .Callback <DownlinkMessage>(d => receivedDownlinkMessage = d); var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.downstreamMessageSender.Object, this.frameCounterStrategyProvider, NullLogger <DefaultClassCDevicesMessageSender> .Instance, TestMeter.Instance); Assert.True(await target.SendAsync(c2dToDeviceMessage)); this.downstreamMessageSender.Verify(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkMessage>()), Times.Once()); EnsureDownlinkIsCorrect(receivedDownlinkMessage, simDevice, c2dToDeviceMessage); this.downstreamMessageSender.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task After_Sending_Class_C_Downstream_Should_Disconnect_Client() { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID, deviceClassType: 'c')); var devEUI = simulatedDevice.DevEUI; // will disconnected client var disconnectedEvent = new SemaphoreSlim(0, 1); this.LoRaDeviceClient.Setup(x => x.Disconnect()) .Callback(() => { disconnectedEvent.Release(); }) .Returns(true); // will check client connection this.LoRaDeviceClient.Setup(x => x.EnsureConnected()) .Returns(true); // will save twin this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .ReturnsAsync(true); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = 10, MessageId = Guid.NewGuid().ToString(), }; var cachedDevice = this.CreateLoRaDevice(simulatedDevice); cachedDevice.KeepAliveTimeout = 3; cachedDevice.LoRaRegion = LoRaRegionType.EU868; cachedDevice.InternalAcceptChanges(); cachedDevice.SetFcntDown(cachedDevice.FCntDown + Constants.MAX_FCNT_UNSAVED_DELTA - 1); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(cachedDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); var target = new DefaultClassCDevicesMessageSender( this.ServerConfiguration, deviceRegistry, this.PacketForwarder, this.FrameCounterUpdateStrategyProvider); Assert.True(await target.SendAsync(c2dToDeviceMessage)); Assert.Single(this.PacketForwarder.DownlinkMessages); await this.EnsureDisconnectedAsync(disconnectedEvent); this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); }
public async Task When_StationEui_Missing_Should_Fail() { var devAddr = new DevAddr(0x023637F8); var appSKey = TestKeys.CreateAppSessionKey(0xABC0200000000000, 0x09); var nwkSKey = TestKeys.CreateNetworkSessionKey(0xABC0200000000000, 0x09); var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: ServerGatewayID)); var devEUI = simDevice.DevEUI; simDevice.SetupJoin(appSKey, nwkSKey, devAddr); this.deviceApi.Setup(x => x.GetPrimaryKeyByEuiAsync(devEUI)) .ReturnsAsync("123"); var twin = simDevice.CreateOTAATwin( desiredProperties: new Dictionary <string, object> { { TwinProperty.RX2DataRate, "10" } }, reportedProperties: new Dictionary <string, object> { { TwinProperty.RX2DataRate, 10 }, { TwinProperty.Region, LoRaRegionType.US915.ToString() }, // OTAA device, already joined { TwinProperty.DevAddr, devAddr.ToString() }, { TwinProperty.AppSKey, appSKey.ToString() }, { TwinProperty.NwkSKey, nwkSKey.ToString() }, }); this.deviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(twin); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = TestPort, MessageId = Guid.NewGuid().ToString(), }; var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.downstreamMessageSender.Object, this.frameCounterStrategyProvider, NullLogger <DefaultClassCDevicesMessageSender> .Instance, TestMeter.Instance); Assert.False(await target.SendAsync(c2dToDeviceMessage)); this.downstreamMessageSender.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task When_Sending_Message_Should_Send_Downlink_To_PacketForwarder(string deviceGatewayID) { var simDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, deviceClassType: 'c', gatewayID: deviceGatewayID)); var devEUI = simDevice.DevEUI; this.deviceApi.Setup(x => x.SearchByDevEUIAsync(devEUI)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(string.Empty, devEUI, "123").AsList())); var twin = simDevice.CreateABPTwin(reportedProperties: new Dictionary <string, object> { { TwinProperty.Region, LoRaRegionType.EU868.ToString() } }); this.deviceClient.Setup(x => x.GetTwinAsync()) .ReturnsAsync(twin); if (string.IsNullOrEmpty(deviceGatewayID)) { // will update the fcnt down this.deviceApi.Setup(x => x.NextFCntDownAsync(devEUI, simDevice.FrmCntDown, 0, this.serverConfiguration.GatewayID)) .ReturnsAsync((ushort)(simDevice.FrmCntDown + 1)); } var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = 10, MessageId = Guid.NewGuid().ToString(), }; this.packetForwarder.Setup(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkPktFwdMessage>())) .Returns(Task.CompletedTask) .Callback <DownlinkPktFwdMessage>(d => { this.EnsureDownlinkIsCorrect(d, simDevice, c2dToDeviceMessage); }); var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.packetForwarder.Object, this.frameCounterStrategyProvider); Assert.True(await target.SendAsync(c2dToDeviceMessage)); this.packetForwarder.Verify(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkPktFwdMessage>()), Times.Once()); this.packetForwarder.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task When_Fails_To_Get_FcntDown_Should_Fail() { var simDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, deviceClassType: 'c')); var devEUI = simDevice.DevEUI; this.deviceApi.Setup(x => x.GetPrimaryKeyByEuiAsync(devEUI)) .ReturnsAsync("123"); var twin = simDevice.CreateABPTwin(reportedProperties: new Dictionary <string, object> { { TwinProperty.Region, LoRaRegionType.EU868.ToString() }, { TwinProperty.LastProcessingStationEui, new StationEui(ulong.MaxValue).ToString() } }); this.deviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(twin); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = TestPort, MessageId = Guid.NewGuid().ToString(), }; // will update the fcnt down this.deviceApi.Setup(x => x.NextFCntDownAsync(devEUI, simDevice.FrmCntDown, 0, this.serverConfiguration.GatewayID)) .ThrowsAsync(new TimeoutException()); var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.downstreamMessageSender.Object, this.frameCounterStrategyProvider, NullLogger <DefaultClassCDevicesMessageSender> .Instance, TestMeter.Instance); _ = await Assert.ThrowsAsync <TimeoutException>(() => target.SendAsync(c2dToDeviceMessage)); this.downstreamMessageSender.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task When_Fport_Is_Not_Set_Should_Fail() { var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", Fport = 10, MessageId = Guid.NewGuid().ToString(), }; var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.packetForwarder.Object, this.frameCounterStrategyProvider); Assert.False(await target.SendAsync(c2dToDeviceMessage)); this.packetForwarder.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task When_Fails_To_Get_FcntDown_Should_Fail() { var simDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, deviceClassType: 'c')); var devEUI = simDevice.DevEUI; this.deviceApi.Setup(x => x.SearchByDevEUIAsync(devEUI)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(string.Empty, devEUI, "123").AsList())); var twin = simDevice.CreateABPTwin(reportedProperties: new Dictionary <string, object> { { TwinProperty.Region, LoRaRegionType.EU868.ToString() } }); this.deviceClient.Setup(x => x.GetTwinAsync()) .ReturnsAsync(twin); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = 10, MessageId = Guid.NewGuid().ToString(), }; // will update the fcnt down this.deviceApi.Setup(x => x.NextFCntDownAsync(devEUI, simDevice.FrmCntDown, 0, this.serverConfiguration.GatewayID)) .ThrowsAsync(new TimeoutException()); var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.packetForwarder.Object, this.frameCounterStrategyProvider); Assert.False(await target.SendAsync(c2dToDeviceMessage)); this.packetForwarder.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
private void EnsureDownlinkIsCorrect(DownlinkPktFwdMessage downlink, SimulatedDevice simDevice, ReceivedLoRaCloudToDeviceMessage sentMessage) { Assert.NotNull(downlink); Assert.NotNull(downlink.Txpk); Assert.True(downlink.Txpk.Imme); Assert.Equal(0, downlink.Txpk.Tmst); Assert.NotEmpty(downlink.Txpk.Data); byte[] downstreamPayloadBytes = Convert.FromBase64String(downlink.Txpk.Data); var downstreamPayload = new LoRaPayloadData(downstreamPayloadBytes); Assert.Equal(sentMessage.Fport, downstreamPayload.GetFPort()); Assert.Equal(downstreamPayload.DevAddr.ToArray(), ConversionHelper.StringToByteArray(simDevice.DevAddr)); var decryptedPayload = downstreamPayload.GetDecryptedPayload(simDevice.AppSKey); Assert.Equal(sentMessage.Payload, Encoding.UTF8.GetString(decryptedPayload)); }
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_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 MessageProcessor_End2End_NoDep_ClassC_CloudToDeviceMessage_SizeLimit_Should_Accept( bool hasMacInC2D) { var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice( 1, deviceClassType: 'c', gatewayID: this.serverConfiguration.GatewayID)); var devEUI = simulatedDevice.DevEUI; this.deviceApi.Setup(x => x.SearchByDevEUIAsync(devEUI)) .ReturnsAsync(new SearchDevicesResult( new IoTHubDeviceInfo(string.Empty, devEUI, "123").AsList())); var twin = simulatedDevice.CreateABPTwin(reportedProperties: new Dictionary <string, object> { { TwinProperty.Region, LoRaRegionType.EU868.ToString() } }); this.deviceClient.Setup(x => x.GetTwinAsync()) .ReturnsAsync(twin); var c2dMessageMacCommand = new DevStatusRequest(); var c2dMessageMacCommandSize = hasMacInC2D ? c2dMessageMacCommand.Length : 0; var datr = this.loRaRegion.DRtoConfiguration[this.loRaRegion.RX2DefaultReceiveWindows.dr].configuration; var c2dPayloadSize = this.loRaRegion.GetMaxPayloadSize(datr) - c2dMessageMacCommandSize - Constants.LORA_PROTOCOL_OVERHEAD_SIZE; var c2dMsgPayload = this.GeneratePayload("123457890", (int)c2dPayloadSize); var c2d = new ReceivedLoRaCloudToDeviceMessage() { DevEUI = devEUI, Payload = c2dMsgPayload, Fport = 1, }; if (hasMacInC2D) { c2d.MacCommands = new[] { c2dMessageMacCommand }; } var cloudToDeviceMessage = c2d.CreateMessage(); var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.PacketForwarder, this.frameCounterStrategyProvider); // Expectations // Verify that C2D message is sent Assert.True(await target.SendAsync(c2d)); // Verify that exactly one C2D message was received Assert.Single(this.PacketForwarder.DownlinkMessages); // Verify donwlink message is correct this.EnsureDownlinkIsCorrect( this.PacketForwarder.DownlinkMessages.First(), simulatedDevice, c2d); // Get C2D message payload var downlinkMessage = this.PacketForwarder.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData( Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(simulatedDevice.AppSKey); // Verify that expected Mac commands are present var expectedMacCommandsCount = 0; if (hasMacInC2D) { expectedMacCommandsCount++; } if (expectedMacCommandsCount > 0) { var macCommands = MacCommand.CreateServerMacCommandFromBytes( simulatedDevice.DevEUI, payloadDataDown.Fopts); Assert.Equal(expectedMacCommandsCount, macCommands.Count); } else { Assert.Null(payloadDataDown.MacCommands); } this.deviceApi.VerifyAll(); this.deviceClient.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 Should_Abandon( bool isConfirmed, bool hasMacInUpstream, bool hasMacInC2D, [CombinatorialValues("SF9BW125", "SF8BW125", "SF7BW125")] string datr) { const int InitialDeviceFcntUp = 9; const int InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = this.CreateLoRaDevice(simulatedDevice); Rxpk rxpk = this.CreateUpstreamRxpk(isConfirmed, hasMacInUpstream, datr, simulatedDevice); if (!hasMacInUpstream) { this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); } var euRegion = RegionManager.EU868; var c2dMessageMacCommand = new DevStatusRequest(); var c2dMessageMacCommandSize = hasMacInC2D ? c2dMessageMacCommand.Length : 0; var upstreamMessageMacCommandSize = 0; string expectedDownlinkDatr; if (hasMacInUpstream) { upstreamMessageMacCommandSize = new LinkCheckAnswer(1, 1).Length; } expectedDownlinkDatr = euRegion.DRtoConfiguration[euRegion.RX2DefaultReceiveWindows.dr].configuration; var c2dPayloadSize = euRegion.GetMaxPayloadSize(expectedDownlinkDatr) - c2dMessageMacCommandSize + 1 // make message too long on purpose - Constants.LORA_PROTOCOL_OVERHEAD_SIZE; var c2dMessagePayload = TestUtils.GeneratePayload("123457890", (int)c2dPayloadSize); var c2dMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = c2dMessagePayload, Fport = 1, }; if (hasMacInC2D) { c2dMessage.MacCommands = new[] { c2dMessageMacCommand }; } var cloudToDeviceMessage = c2dMessage.CreateMessage(); this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .Callback <TimeSpan>((_) => { this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync((Message)null); }) .ReturnsAsync(cloudToDeviceMessage); this.LoRaDeviceClient.Setup(x => x.AbandonAsync(cloudToDeviceMessage)) .ReturnsAsync(true); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(loraDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); var request = this.CreateWaitableRequest(rxpk, startTimeOffset: TestUtils.GetStartTimeOffsetForSecondWindow(), constantElapsedTime: TimeSpan.FromMilliseconds(1002)); messageProcessor.DispatchRequest(request); // Expectations // 1. Message was sent to IoT Hub Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); // 2. Return is downstream message Assert.NotNull(request.ResponseDownlink); Assert.Equal(expectedDownlinkDatr, request.ResponseDownlink.Txpk.Datr); var downlinkMessage = this.PacketForwarder.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(loraDevice.AppSKey); // 3. Fpending flag is set Assert.Equal((byte)FctrlEnum.FpendingOrClassB, payloadDataDown.Fctrl.Span[0] & (byte)FctrlEnum.FpendingOrClassB); Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // Expected Mac command is present if (hasMacInUpstream) { // Possible problem: manually casting frmPayload to array. No reversal. var frmPayload = payloadDataDown.Frmpayload.ToArray(); var macCommands = MacCommand.CreateServerMacCommandFromBytes(simulatedDevice.DevEUI, frmPayload); Assert.Single(macCommands); Assert.IsType <LinkCheckAnswer>(macCommands.First()); } else { Assert.Null(payloadDataDown.MacCommands); } this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); }
public async Task When_Device_Checks_For_C2D_Message_Uses_Available_Time( ReceiveWindowNumber preferredWindow, int sendEventDurationInMs, int checkMinDuration, int checkMaxDuration, bool expectingSecondWindow) { const int PayloadFcnt = 10; const int InitialDeviceFcntUp = 9; const int InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var devAddr = simulatedDevice.DevAddr; var devEUI = simulatedDevice.DevEUI; 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.IsInRange <TimeSpan>(TimeSpan.FromMilliseconds(checkMinDuration), TimeSpan.FromMilliseconds(checkMaxDuration), Moq.Range.Inclusive))) .ReturnsAsync(cloudToDeviceMessage); LoRaDeviceClient.Setup(x => x.ReceiveAsync(LoRaOperationTimeWatcher.MinimumAvailableTimeToCheckForCloudMessage)) .Returns(EmptyAdditionalMessageReceiveAsync); // 2nd cloud to device message does not return anything LoRaDeviceClient.Setup(x => x.CompleteAsync(cloudToDeviceMessage)) .ReturnsAsync(true); var loRaDevice = CreateLoRaDevice(simulatedDevice); loRaDevice.PreferredWindow = preferredWindow; 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: sendEventDurationInMs > 0 ? TimeSpan.FromMilliseconds(sendEventDurationInMs) : TimeSpan.FromMilliseconds(100)); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.NotNull(request.ResponseDownlink); var downlinkMessage = Assert.Single(DownstreamMessageSender.DownlinkMessages); Assert.Equal(LoRaDeviceClassType.A, downlinkMessage.DeviceClassType); Assert.Equal(loRaDevice.ReportedRXDelay, downlinkMessage.LnsRxDelay); if (expectingSecondWindow) { Assert.Null(downlinkMessage.Rx1); } else { Assert.NotNull(downlinkMessage.Rx1); } LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }
public async Task When_OTAA_Join_Then_Sends_Upstream_DirectMethod_Should_Send_Downstream(string deviceGatewayID) { var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: deviceGatewayID)); this.LoRaDeviceClient.Setup(x => x.GetTwinAsync()) .ReturnsAsync(simDevice.CreateOTAATwin()); var savedAppSKey = string.Empty; var savedNwkSKey = string.Empty; var savedDevAddr = string.Empty; this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .ReturnsAsync(true) .Callback <TwinCollection>((t) => { savedAppSKey = t[TwinProperty.AppSKey]; savedNwkSKey = t[TwinProperty.NwkSKey]; savedDevAddr = t[TwinProperty.DevAddr]; Assert.NotEmpty(savedAppSKey); Assert.NotEmpty(savedNwkSKey); Assert.NotEmpty(savedDevAddr); }); this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); if (deviceGatewayID == null) { this.LoRaDeviceApi.Setup(x => x.ExecuteFunctionBundlerAsync(simDevice.DevEUI, It.IsNotNull <FunctionBundlerRequest>())) .ReturnsAsync(new FunctionBundlerResult()); } this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, simDevice.DevEUI, simDevice.AppEUI, It.IsNotNull <string>())) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simDevice.DevAddr, simDevice.DevEUI, "123").AsList())); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewMemoryCache(), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); var messageDispatcher = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); var joinRxpk = simDevice.CreateJoinRequest().SerializeUplink(simDevice.AppKey).Rxpk[0]; var joinRequest = this.CreateWaitableRequest(joinRxpk); messageDispatcher.DispatchRequest(joinRequest); Assert.True(await joinRequest.WaitCompleteAsync()); Assert.True(joinRequest.ProcessingSucceeded); simDevice.SetupJoin(savedAppSKey, savedNwkSKey, savedDevAddr); var request = this.CreateWaitableRequest(simDevice.CreateUnconfirmedMessageUplink("1").Rxpk[0]); messageDispatcher.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); var classCSender = new DefaultClassCDevicesMessageSender( this.ServerConfiguration, deviceRegistry, this.PacketForwarder, this.FrameCounterUpdateStrategyProvider); var c2d = new ReceivedLoRaCloudToDeviceMessage() { DevEUI = simDevice.DevEUI, MessageId = Guid.NewGuid().ToString(), Payload = "aaaa", Fport = 14, }; if (string.IsNullOrEmpty(deviceGatewayID)) { this.LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(simDevice.DevEUI, simDevice.FrmCntDown, 0, this.ServerConfiguration.GatewayID)) .ReturnsAsync((ushort)(simDevice.FrmCntDown + 1)); } Assert.True(await classCSender.SendAsync(c2d)); Assert.Equal(2, this.PacketForwarder.DownlinkMessages.Count); var downstreamMsg = this.PacketForwarder.DownlinkMessages[1]; TestLogger.Log($"appSKey: {simDevice.AppSKey}, nwkSKey: {simDevice.NwkSKey}"); byte[] downstreamPayloadBytes = Convert.FromBase64String(downstreamMsg.Txpk.Data); var downstreamPayload = new LoRaPayloadData(downstreamPayloadBytes); Assert.Equal(1, downstreamPayload.GetFcnt()); Assert.Equal(c2d.Fport, downstreamPayload.GetFPort()); Assert.Equal(downstreamPayload.DevAddr.ToArray(), ConversionHelper.StringToByteArray(savedDevAddr)); var decryptedPayload = downstreamPayload.GetDecryptedPayload(simDevice.AppSKey); Assert.Equal(c2d.Payload, Encoding.UTF8.GetString(decryptedPayload)); this.LoRaDeviceApi.VerifyAll(); this.LoRaDeviceClient.VerifyAll(); }
private static void EnsureDownlinkIsCorrect(DownlinkMessage downlink, SimulatedDevice simDevice, ReceivedLoRaCloudToDeviceMessage sentMessage) { Assert.NotNull(downlink); Assert.False(downlink.Data.IsEmpty); var downstreamPayloadBytes = downlink.Data; var downstreamPayload = new LoRaPayloadData(downstreamPayloadBytes); Assert.Equal(sentMessage.Fport, downstreamPayload.Fport); Assert.Equal(downstreamPayload.DevAddr, simDevice.DevAddr); var decryptedPayload = downstreamPayload.GetDecryptedPayload(simDevice.AppSKey.Value); Assert.Equal(sentMessage.Payload, Encoding.UTF8.GetString(decryptedPayload)); }
public async Task When_Has_Custom_RX2DR_Should_Send_Correctly() { var devAddr = new DevAddr(0x023637F8); var appSKey = TestKeys.CreateAppSessionKey(0xABC0200000000000, 0x09); var nwkSKey = TestKeys.CreateNetworkSessionKey(0xABC0200000000000, 0x09); var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: ServerGatewayID)); var devEUI = simDevice.DevEUI; simDevice.SetupJoin(appSKey, nwkSKey, devAddr); this.deviceApi.Setup(x => x.GetPrimaryKeyByEuiAsync(devEUI)) .ReturnsAsync("123"); var twin = simDevice.CreateOTAATwin( desiredProperties: new Dictionary <string, object> { { TwinProperty.RX2DataRate, "10" } }, reportedProperties: new Dictionary <string, object> { { TwinProperty.RX2DataRate, 10 }, { TwinProperty.Region, LoRaRegionType.US915.ToString() }, // OTAA device, already joined { TwinProperty.DevAddr, devAddr.ToString() }, { TwinProperty.AppSKey, appSKey.ToString() }, { TwinProperty.NwkSKey, nwkSKey.ToString() }, { TwinProperty.LastProcessingStationEui, new StationEui(ulong.MaxValue).ToString() } }); this.deviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(twin); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = TestPort, MessageId = Guid.NewGuid().ToString(), }; DownlinkMessage receivedDownlinkMessage = null; this.downstreamMessageSender.Setup(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkMessage>())) .Returns(Task.CompletedTask) .Callback <DownlinkMessage>(d => receivedDownlinkMessage = d); var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.downstreamMessageSender.Object, this.frameCounterStrategyProvider, NullLogger <DefaultClassCDevicesMessageSender> .Instance, TestMeter.Instance); Assert.True(await target.SendAsync(c2dToDeviceMessage)); this.downstreamMessageSender.Verify(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkMessage>()), Times.Once()); EnsureDownlinkIsCorrect(receivedDownlinkMessage, simDevice, c2dToDeviceMessage); Assert.Equal(DataRateIndex.DR10, receivedDownlinkMessage.Rx2.DataRate); Assert.Equal(Hertz.Mega(923.3), receivedDownlinkMessage.Rx2.Frequency); this.downstreamMessageSender.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task When_Getting_C2D_Message_Fails_To_Resolve_Fcnt_Down_Should_Drop_Message_And_Return_Null() { const uint initialFcntDown = 5; const uint initialFcntUp = 21; const uint payloadFcnt = 23; var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: null)); simulatedDevice.FrmCntUp = initialFcntUp; simulatedDevice.FrmCntDown = initialFcntDown; var devEUI = simulatedDevice.LoRaDevice.DeviceID; var devAddr = simulatedDevice.LoRaDevice.DevAddr; // message will be sent this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); var c2dMessage = new ReceivedLoRaCloudToDeviceMessage() { Fport = 1 }; var cloudToDeviceMessage = c2dMessage.CreateMessage(); // C2D message will be retrieved this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync(cloudToDeviceMessage); // C2D message will be abandonned this.LoRaDeviceClient.Setup(x => x.AbandonAsync(cloudToDeviceMessage)) .ReturnsAsync(true); // getting the fcnt down will return 0! this.LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(devEUI, initialFcntDown, payloadFcnt, this.ServerConfiguration.GatewayID)) .ReturnsAsync((ushort)0); var cachedDevice = this.CreateLoRaDevice(simulatedDevice); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(cachedDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageDispatcher = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); // sends unconfirmed message var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("hello", fcnt: payloadFcnt); var rxpk = unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]; var unconfirmedRequest = this.CreateWaitableRequest(rxpk); messageDispatcher.DispatchRequest(unconfirmedRequest); Assert.True(await unconfirmedRequest.WaitCompleteAsync()); Assert.Null(unconfirmedRequest.ResponseDownlink); var cachedDevices = deviceRegistry.InternalGetCachedDevicesForDevAddr(simulatedDevice.DevAddr); Assert.True(cachedDevices.TryGetValue(devEUI, out var loRaDevice)); // fcnt down did not change Assert.Equal(initialFcntDown, loRaDevice.FCntDown); // fcnt up changed Assert.Equal(unconfirmedMessagePayload.GetFcnt(), loRaDevice.FCntUp); this.LoRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsAny <TimeSpan>()), Times.Once()); this.LoRaDeviceClient.Verify(x => x.AbandonAsync(It.IsAny <Message>()), Times.Once()); this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); }
public async Task When_OTAA_Join_Then_Sends_Upstream_DirectMethod_Should_Send_Downstream(string deviceGatewayID) { var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: deviceGatewayID)); LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(simDevice.CreateOTAATwin()); AppSessionKey? savedAppSKey = null; NetworkSessionKey?savedNwkSKey = null; var savedDevAddr = string.Empty; LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true) .Callback <TwinCollection, CancellationToken>((t, _) => { savedAppSKey = AppSessionKey.Parse(t[TwinProperty.AppSKey].Value); savedNwkSKey = NetworkSessionKey.Parse(t[TwinProperty.NwkSKey].Value); savedDevAddr = t[TwinProperty.DevAddr]; }); LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); if (deviceGatewayID == null) { LoRaDeviceApi.Setup(x => x.ExecuteFunctionBundlerAsync(simDevice.DevEUI, It.IsNotNull <FunctionBundlerRequest>())) .ReturnsAsync(new FunctionBundlerResult()); } 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 payloadata = simDevice.CreateJoinRequest(); using var joinRequest = CreateWaitableRequest(payloadata); joinRequest.SetStationEui(new StationEui(ulong.MaxValue)); messageDispatcher.DispatchRequest(joinRequest); Assert.True(await joinRequest.WaitCompleteAsync()); Assert.True(joinRequest.ProcessingSucceeded); Assert.NotNull(savedAppSKey); Assert.NotNull(savedNwkSKey); Assert.NotEmpty(savedDevAddr); simDevice.SetupJoin(savedAppSKey.Value, savedNwkSKey.Value, DevAddr.Parse(savedDevAddr)); using var request = CreateWaitableRequest(simDevice.CreateUnconfirmedDataUpMessage("1")); request.SetStationEui(new StationEui(ulong.MaxValue)); messageDispatcher.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); 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.App14, }; if (string.IsNullOrEmpty(deviceGatewayID)) { LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(simDevice.DevEUI, simDevice.FrmCntDown, 0, ServerConfiguration.GatewayID)) .ReturnsAsync((ushort)(simDevice.FrmCntDown + 1)); } Assert.True(await classCSender.SendAsync(c2d)); Assert.Equal(2, DownstreamMessageSender.DownlinkMessages.Count); var downstreamMsg = DownstreamMessageSender.DownlinkMessages[1]; TestLogger.Log($"appSKey: {simDevice.AppSKey}, nwkSKey: {simDevice.NwkSKey}"); var downstreamPayloadBytes = downstreamMsg.Data; var downstreamPayload = new LoRaPayloadData(downstreamPayloadBytes); Assert.Equal(1, downstreamPayload.Fcnt); Assert.Equal(c2d.Fport, downstreamPayload.Fport); Assert.Equal(downstreamPayload.DevAddr, DevAddr.Parse(savedDevAddr)); var decryptedPayload = downstreamPayload.GetDecryptedPayload(simDevice.AppSKey.Value); Assert.Equal(c2d.Payload, Encoding.UTF8.GetString(decryptedPayload)); LoRaDeviceApi.VerifyAll(); LoRaDeviceClient.VerifyAll(); }
public async Task Should_Reject( bool isConfirmed, bool hasMacInUpstream, bool hasMacInC2D, [CombinatorialValues("SF10BW125", "SF9BW125", "SF8BW125", "SF7BW125")] string datr) { const int InitialDeviceFcntUp = 9; const int InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = this.CreateLoRaDevice(simulatedDevice); Rxpk rxpk = this.CreateUpstreamRxpk(isConfirmed, hasMacInUpstream, datr, simulatedDevice); if (!hasMacInUpstream) { this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); } var euRegion = RegionManager.EU868; var c2dMessageMacCommand = new DevStatusRequest(); var c2dMessageMacCommandSize = hasMacInC2D ? c2dMessageMacCommand.Length : 0; var upstreamMessageMacCommandSize = 0; string expectedDownlinkDatr; if (hasMacInUpstream) { upstreamMessageMacCommandSize = new LinkCheckAnswer(1, 1).Length; } expectedDownlinkDatr = datr; var c2dPayloadSize = euRegion.GetMaxPayloadSize(expectedDownlinkDatr) - c2dMessageMacCommandSize + 1 // make message too long on purpose - Constants.LORA_PROTOCOL_OVERHEAD_SIZE; var c2dMessagePayload = TestUtils.GeneratePayload("123457890", (int)c2dPayloadSize); var c2dMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = c2dMessagePayload, Fport = 1, }; if (hasMacInC2D) { c2dMessage.MacCommands = new[] { c2dMessageMacCommand }; } var cloudToDeviceMessage = c2dMessage.CreateMessage(); this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync(cloudToDeviceMessage); this.LoRaDeviceClient.Setup(x => x.RejectAsync(cloudToDeviceMessage)) .ReturnsAsync(true); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(loraDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); var request = this.CreateWaitableRequest(rxpk); messageProcessor.DispatchRequest(request); // Expectations // 1. Message was sent to IoT Hub Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); var shouldHaveADownlink = isConfirmed || hasMacInUpstream; // 2. Return is downstream message ONLY // if is confirmed or had Mac commands in upstream message if (shouldHaveADownlink) { Assert.NotNull(request.ResponseDownlink); Assert.Equal(expectedDownlinkDatr, request.ResponseDownlink.Txpk.Datr); var downlinkMessage = this.PacketForwarder.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(loraDevice.AppSKey); Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); if (hasMacInUpstream) { Assert.Equal(new LinkCheckAnswer(1, 1).Length, payloadDataDown.Frmpayload.Length); Assert.Equal(0, payloadDataDown.GetFPort()); } } else { Assert.Null(request.ResponseDownlink); } this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); }
public async Task Should_Accept( bool isConfirmed, bool hasMacInUpstream, bool hasMacInC2D, bool isTooLongForUpstreamMacCommandInAnswer, bool isSendingInRx2, [CombinatorialValues("SF10BW125", "SF9BW125", "SF8BW125", "SF7BW125")] string datr) { const int InitialDeviceFcntUp = 9; const int InitialDeviceFcntDown = 20; // This scenario makes no sense if (hasMacInUpstream && isTooLongForUpstreamMacCommandInAnswer) { return; } var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = this.CreateLoRaDevice(simulatedDevice); Rxpk rxpk = this.CreateUpstreamRxpk(isConfirmed, hasMacInUpstream, datr, simulatedDevice); if (!hasMacInUpstream) { this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); } var euRegion = RegionManager.EU868; var c2dMessageMacCommand = new DevStatusRequest(); var c2dMessageMacCommandSize = hasMacInC2D ? c2dMessageMacCommand.Length : 0; var upstreamMessageMacCommandSize = 0; string expectedDownlinkDatr; if (hasMacInUpstream && !isTooLongForUpstreamMacCommandInAnswer) { upstreamMessageMacCommandSize = new LinkCheckAnswer(1, 1).Length; } if (isSendingInRx2) { expectedDownlinkDatr = euRegion.DRtoConfiguration[euRegion.RX2DefaultReceiveWindows.dr].configuration; } else { expectedDownlinkDatr = datr; } var c2dPayloadSize = euRegion.GetMaxPayloadSize(expectedDownlinkDatr) - c2dMessageMacCommandSize - upstreamMessageMacCommandSize - Constants.LORA_PROTOCOL_OVERHEAD_SIZE; var c2dMessagePayload = TestUtils.GeneratePayload("123457890", (int)c2dPayloadSize); var c2dMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = c2dMessagePayload, Fport = 1, }; if (hasMacInC2D) { c2dMessage.MacCommands = new[] { c2dMessageMacCommand }; } var cloudToDeviceMessage = c2dMessage.CreateMessage(); this.LoRaDeviceClient.SetupSequence(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync(cloudToDeviceMessage) .ReturnsAsync((Message)null); this.LoRaDeviceClient.Setup(x => x.CompleteAsync(cloudToDeviceMessage)) .ReturnsAsync(true); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(loraDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); var startTimeOffset = isSendingInRx2 ? TestUtils.GetStartTimeOffsetForSecondWindow() : TimeSpan.Zero; var request = this.CreateWaitableRequest(rxpk, startTimeOffset: startTimeOffset); messageProcessor.DispatchRequest(request); // Expectations // 1. Message was sent to IoT Hub Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); // 2. Return is downstream message Assert.NotNull(request.ResponseDownlink); Assert.Equal(expectedDownlinkDatr, request.ResponseDownlink.Txpk.Datr); // Get downlink message var downlinkMessage = this.PacketForwarder.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(loraDevice.AppSKey); // 3. downlink message payload contains expected message type and DevAddr Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // 4. Expected Mac commands are present var expectedMacCommandsCount = 0; if (hasMacInC2D) { expectedMacCommandsCount++; } if (hasMacInUpstream && !isTooLongForUpstreamMacCommandInAnswer) { expectedMacCommandsCount++; } if (expectedMacCommandsCount > 0) { // Possible problem: Manually casting payloadDataDown.Fopts to array and reversing it var macCommands = MacCommand.CreateServerMacCommandFromBytes(simulatedDevice.DevEUI, payloadDataDown.Fopts.ToArray().Reverse().ToArray()); Assert.Equal(expectedMacCommandsCount, macCommands.Count); } else { Assert.Null(payloadDataDown.MacCommands); } this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); }
public async Task When_Has_Custom_RX2DR_Should_Send_Correctly() { const string devAddr = "023637F8"; const string appSKey = "ABC02000000000000000000000000009ABC02000000000000000000000000009"; const string nwkSKey = "ABC02000000000000000000000000009ABC02000000000000000000000000009"; var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: ServerGatewayID)); var devEUI = simDevice.DevEUI; simDevice.SetupJoin(appSKey, nwkSKey, devAddr); this.deviceApi.Setup(x => x.SearchByDevEUIAsync(devEUI)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(string.Empty, devEUI, "123").AsList())); var twin = simDevice.CreateOTAATwin( desiredProperties: new Dictionary <string, object> { { TwinProperty.RX2DataRate, "10" } }, reportedProperties: new Dictionary <string, object> { { TwinProperty.RX2DataRate, 10 }, { TwinProperty.Region, LoRaRegionType.US915.ToString() }, // OTAA device, already joined { TwinProperty.DevAddr, devAddr }, { TwinProperty.AppSKey, appSKey }, { TwinProperty.NwkSKey, nwkSKey } }); this.deviceClient.Setup(x => x.GetTwinAsync()) .ReturnsAsync(twin); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = 10, MessageId = Guid.NewGuid().ToString(), }; this.packetForwarder.Setup(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkPktFwdMessage>())) .Returns(Task.CompletedTask) .Callback <DownlinkPktFwdMessage>(d => { this.EnsureDownlinkIsCorrect(d, simDevice, c2dToDeviceMessage); Assert.Equal("SF10BW500", d.Txpk.Datr); Assert.Equal(0L, d.Txpk.Tmst); Assert.Equal(923.3, d.Txpk.Freq); Assert.True(d.Txpk.Imme); }); var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.packetForwarder.Object, this.frameCounterStrategyProvider); Assert.True(await target.SendAsync(c2dToDeviceMessage)); this.packetForwarder.Verify(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkPktFwdMessage>()), Times.Once()); this.packetForwarder.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task When_ABP_Sends_Upstream_Followed_By_DirectMethod_Should_Send_Upstream_And_Downstream(string deviceGatewayID, uint fcntDownFromTwin, uint fcntDelta) { const uint payloadFcnt = 2; // to avoid relax mode reset var simDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID, deviceClassType: 'c'), frmCntDown: fcntDownFromTwin); this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .ReturnsAsync(true); var twin = simDevice.CreateABPTwin(reportedProperties: new Dictionary <string, object> { { TwinProperty.Region, LoRaRegionType.EU868.ToString() } }); this.LoRaDeviceClient.Setup(x => x.GetTwinAsync()) .ReturnsAsync(twin); this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); this.LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simDevice.DevAddr)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simDevice.DevAddr, simDevice.DevEUI, "123").AsList())); if (deviceGatewayID == null) { this.LoRaDeviceApi.Setup(x => x.ExecuteFunctionBundlerAsync(simDevice.DevEUI, It.IsNotNull <FunctionBundlerRequest>())) .ReturnsAsync(new FunctionBundlerResult()); } var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewMemoryCache(), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); var messageDispatcher = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); var request = this.CreateWaitableRequest(simDevice.CreateUnconfirmedMessageUplink("1", fcnt: payloadFcnt).Rxpk[0]); messageDispatcher.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); // wait until cache has been updated await Task.Delay(50); // Adds fcntdown to device, simulating multiple downstream calls Assert.True(deviceRegistry.InternalGetCachedDevicesForDevAddr(simDevice.DevAddr).TryGetValue(simDevice.DevEUI, out var loRaDevice)); loRaDevice.SetFcntDown(fcntDelta + loRaDevice.FCntDown); var classCSender = new DefaultClassCDevicesMessageSender( this.ServerConfiguration, deviceRegistry, this.PacketForwarder, this.FrameCounterUpdateStrategyProvider); var c2d = new ReceivedLoRaCloudToDeviceMessage() { DevEUI = simDevice.DevEUI, MessageId = Guid.NewGuid().ToString(), Payload = "aaaa", Fport = 18, }; var expectedFcntDown = fcntDownFromTwin + Constants.MAX_FCNT_UNSAVED_DELTA + fcntDelta; if (string.IsNullOrEmpty(deviceGatewayID)) { this.LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(simDevice.DevEUI, fcntDownFromTwin + fcntDelta, 0, this.ServerConfiguration.GatewayID)) .ReturnsAsync((ushort)expectedFcntDown); } Assert.True(await classCSender.SendAsync(c2d)); Assert.Single(this.PacketForwarder.DownlinkMessages); var downstreamMsg = this.PacketForwarder.DownlinkMessages[0]; byte[] downstreamPayloadBytes = Convert.FromBase64String(downstreamMsg.Txpk.Data); var downstreamPayload = new LoRaPayloadData(downstreamPayloadBytes); Assert.Equal(expectedFcntDown, downstreamPayload.GetFcnt()); Assert.Equal(c2d.Fport, downstreamPayload.GetFPort()); Assert.Equal(downstreamPayload.DevAddr.ToArray(), ConversionHelper.StringToByteArray(simDevice.DevAddr)); var decryptedPayload = downstreamPayload.GetDecryptedPayload(simDevice.AppSKey); Assert.Equal(c2d.Payload, Encoding.UTF8.GetString(decryptedPayload)); Assert.Equal(expectedFcntDown, loRaDevice.FCntDown); Assert.Equal(payloadFcnt, loRaDevice.FCntUp); Assert.False(loRaDevice.HasFrameCountChanges); this.LoRaDeviceApi.VerifyAll(); this.LoRaDeviceClient.VerifyAll(); }
public async Task When_Getting_C2D_Message_Fails_To_Resolve_Fcnt_Down_Should_Abandon_Message_And_Return_Null() { const uint initialFcntDown = 5; const uint initialFcntUp = 21; const uint payloadFcnt = 23; var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: null)) { FrmCntUp = initialFcntUp, FrmCntDown = initialFcntDown }; var devEui = simulatedDevice.LoRaDevice.DevEui; // message will be sent LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); var c2dMessage = new ReceivedLoRaCloudToDeviceMessage() { Fport = FramePorts.App1 }; using var cloudToDeviceMessage = c2dMessage.CreateMessage(); // C2D message will be retrieved LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync(cloudToDeviceMessage); // C2D message will not be abandoned // getting the fcnt down will return 0! LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(devEui, initialFcntDown, payloadFcnt, ServerConfiguration.GatewayID)) .ReturnsAsync((ushort)0); var cachedDevice = CreateLoRaDevice(simulatedDevice); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(cachedDevice); 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("hello", fcnt: payloadFcnt); using var unconfirmedRequest = CreateWaitableRequest(unconfirmedMessagePayload); messageDispatcher.DispatchRequest(unconfirmedRequest); Assert.True(await unconfirmedRequest.WaitCompleteAsync()); Assert.Null(unconfirmedRequest.ResponseDownlink); Assert.True(loraDeviceCache.TryGetForPayload(unconfirmedRequest.Payload, out var loRaDevice)); // fcnt down did not change Assert.Equal(initialFcntDown, loRaDevice.FCntDown); // fcnt up changed Assert.Equal(unconfirmedMessagePayload.Fcnt, loRaDevice.FCntUp); LoRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsAny <TimeSpan>()), Times.Once()); LoRaDeviceClient.Verify(x => x.AbandonAsync(It.IsAny <Message>()), Times.Once()); LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); }