Exemple #1
0
        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();
        }
Exemple #2
0
        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();
        }
Exemple #3
0
        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();
        }
Exemple #4
0
        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();
        }
Exemple #5
0
        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();
        }
Exemple #6
0
        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();
        }
Exemple #7
0
        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();
        }
Exemple #8
0
        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();
        }
Exemple #9
0
        public async Task OTAA_Unconfirmed_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);
            }

            var cloudToDeviceMessageBody = new ReceivedLoRaCloudToDeviceMessage()
            {
                Fport   = TestPort,
                Payload = "c2d"
            };

            using var cloudToDeviceMessage = cloudToDeviceMessageBody.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);

            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);
            }
        }
        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: ServerConfiguration.GatewayID),
                frmCntUp: InitialDeviceFcntUp,
                frmCntDown: InitialDeviceFcntDown);

            var loraDevice = CreateLoRaDevice(simulatedDevice);

            var(radioMetaData, loraPayload) = CreateUpstreamMessage(isConfirmed, hasMacInUpstream, LoRaDataRate.Parse(datr), simulatedDevice);

            if (!hasMacInUpstream)
            {
                LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
                .ReturnsAsync(true);
            }

            var region = TestUtils.TestRegion;
            var c2dMessageMacCommand     = new DevStatusRequest();
            var c2dMessageMacCommandSize = hasMacInC2D ? c2dMessageMacCommand.Length : 0;
            var expectedDownlinkDatr     = region.GetDataRateIndex(LoRaDataRate.Parse(datr));

            var c2dPayloadSize = region.GetMaxPayloadSize(expectedDownlinkDatr)
                                 - c2dMessageMacCommandSize
                                 + 1 // make message too long on purpose
                                 - Constants.LoraProtocolOverheadSize;

            var c2dMessagePayload = TestUtils.GeneratePayload("123457890", (int)c2dPayloadSize);

            var c2dMessage = new ReceivedLoRaCloudToDeviceMessage()
            {
                Payload = c2dMessagePayload,
                Fport   = FramePorts.App1,
            };

            if (hasMacInC2D)
            {
                c2dMessage.MacCommands.Add(c2dMessageMacCommand);
            }

            using var cloudToDeviceMessage = c2dMessage.CreateMessage();

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

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

            using var request = CreateWaitableRequest(radioMetaData, loraPayload, constantElapsedTime: TimeSpan.Zero);
            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);

                // TODO CHANGE THIS WHEN MOVING RXPK in #1086
                // Assert.Equal(expectedDownlinkDatr, request.ResponseDownlink.Txpk.Datr);

                var downlinkMessage = DownstreamMessageSender.DownlinkMessages[0];
                var payloadDataDown = new LoRaPayloadData(downlinkMessage.Data);
                payloadDataDown.Serialize(loraDevice.AppSKey.Value);

                Assert.Equal(payloadDataDown.DevAddr, loraDevice.DevAddr);
                Assert.Equal(MacMessageType.UnconfirmedDataDown, payloadDataDown.MessageType);

                if (hasMacInUpstream)
                {
                    Assert.Equal(new LinkCheckAnswer(1, 1).Length, payloadDataDown.Frmpayload.Length);
                    Assert.Equal(FramePort.MacCommand, payloadDataDown.Fport);
                }
            }
            else
            {
                Assert.Null(request.ResponseDownlink);
            }

            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }