Example #1
0
        public async Task After_Sending_Class_C_Downstream_Should_Disconnect_Client()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1,
                                                                                     gatewayID: ServerConfiguration.GatewayID,
                                                                                     deviceClassType: 'c'));
            var devEui = simulatedDevice.DevEUI;

            // will disconnected client
            using var disconnectedEvent = new SemaphoreSlim(0, 1);
            LoRaDeviceClient.Setup(x => x.Disconnect())
            .Callback(() =>
            {
                disconnectedEvent.Release();
            })
            .Returns(true);

            // will check client connection
            LoRaDeviceClient.Setup(x => x.EnsureConnected())
            .Returns(true);

            // will save twin
            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true);

            var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage()
            {
                Payload   = "hello",
                DevEUI    = devEui,
                Fport     = FramePorts.App10,
                MessageId = Guid.NewGuid().ToString(),
            };

            var cachedDevice = CreateLoRaDevice(simulatedDevice);

            cachedDevice.KeepAliveTimeout = 3;
            cachedDevice.LoRaRegion       = LoRaRegionType.EU868;
            cachedDevice.InternalAcceptChanges();
            cachedDevice.SetFcntDown(cachedDevice.FCntDown + Constants.MaxFcntUnsavedDelta - 1);
            cachedDevice.SetLastProcessingStationEui(new StationEui(ulong.MaxValue));

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(cachedDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            var target = new DefaultClassCDevicesMessageSender(
                ServerConfiguration,
                deviceRegistry,
                DownstreamMessageSender,
                FrameCounterUpdateStrategyProvider,
                new TestOutputLogger <DefaultClassCDevicesMessageSender>(testOutputHelper),
                TestMeter.Instance);

            Assert.True(await target.SendAsync(c2dToDeviceMessage));
            Assert.Single(DownstreamMessageSender.DownlinkMessages);

            await EnsureDisconnectedAsync(disconnectedEvent);

            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task When_No_Decoder_Is_Defined_Sends_Raw_Payload(string deviceGatewayID, string msgPayload, string sensorDecoder)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID));
            var loRaDevice      = CreateLoRaDevice(simulatedDevice);

            loRaDevice.SensorDecoder = sensorDecoder;

            // message will be sent
            LoRaDeviceTelemetry loRaDeviceTelemetry = null;

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => loRaDeviceTelemetry = t)
            .ReturnsAsync(true);

            // C2D message will be checked
            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            if (string.IsNullOrEmpty(deviceGatewayID))
            {
                // multi GW will reset
                LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(It.IsNotNull <DevEui>(), It.IsAny <uint>(), It.IsNotNull <string>()))
                .ReturnsAsync(true);
            }

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loRaDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            // Send to message processor
            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage(msgPayload, fcnt: 1);

            using var request = CreateWaitableRequest(unconfirmedMessagePayload);
            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.Null(request.ResponseDownlink);

            Assert.NotNull(loRaDeviceTelemetry);
            Assert.IsType <UndecodedPayload>(loRaDeviceTelemetry.Data);
            var undecodedPayload = (UndecodedPayload)loRaDeviceTelemetry.Data;
            var rawPayload       = Convert.ToBase64String(Encoding.UTF8.GetBytes(msgPayload));

            Assert.Equal(rawPayload, loRaDeviceTelemetry.Rawdata);
            Assert.Equal(rawPayload, undecodedPayload.Value);

            // Validate json
            var actualJsonTelemetry   = JsonConvert.SerializeObject(loRaDeviceTelemetry, Formatting.None);
            var expectedTelemetryJson = $"{{\"time\":100000,\"tmms\":100000,\"freq\":868.3,\"chan\":2,\"rfch\":1,\"modu\":\"LoRa\",\"datr\":\"SF10BW125\",\"rssi\":2.0,\"lsnr\":0.1,\"data\":{{\"value\":\"{rawPayload}\"}},\"port\":1,\"fcnt\":1,\"edgets\":{loRaDeviceTelemetry.Edgets},\"rawdata\":\"{rawPayload}\",\"eui\":\"0000000000000001\",\"gatewayid\":\"test-gateway\",\"stationeui\":\"0000000000000000\"}}";

            Assert.Equal(expectedTelemetryJson, actualJsonTelemetry);

            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task When_Api_Returns_DevAlreadyUsed_Should_Return_Null()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: null));
            var joinRequest     = simulatedDevice.CreateJoinRequest();

            var devEui = simulatedDevice.LoRaDevice.DevEui;

            // Lora device api will be search by devices with matching deveui,
            LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequest.DevNonce))
            .ReturnsAsync(new SearchDevicesResult()
            {
                IsDevNonceAlreadyUsed = true
            });

            using var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.Null(request.ResponseDownlink);

            LoRaDeviceApi.VerifyAll();
            LoRaDeviceClient.VerifyAll();
        }
Example #4
0
        public async Task When_Device_Is_Loaded_Should_Disconnect_After_Sending_Data()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerGatewayID));

            // will search for the device by devAddr
            LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simulatedDevice.DevAddr, simulatedDevice.DevEUI, "ada").AsList()));

            // will read the device twins
            var twin = simulatedDevice.CreateABPTwin(desiredProperties: new Dictionary <string, object>
            {
                { TwinProperty.KeepAliveTimeout, 3 }
            });

            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None))
            .ReturnsAsync(twin);

            // message will be sent
            LoRaDeviceTelemetry loRaDeviceTelemetry = null;

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => loRaDeviceTelemetry = t)
            .ReturnsAsync(true);

            // C2D message will be checked
            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            // will check client connection
            LoRaDeviceClient.Setup(x => x.EnsureConnected())
            .Returns(true);

            // will disconnected client
            using var disconnectedEvent = new SemaphoreSlim(0, 1);
            LoRaDeviceClient.Setup(x => x.Disconnect())
            .Callback(() => disconnectedEvent.Release())
            .Returns(true);

            using var cache          = NewMemoryCache();
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("hello");

            using var request = CreateWaitableRequest(unconfirmedMessagePayload);
            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingSucceeded);

            await EnsureDisconnectedAsync(disconnectedEvent, (int)TimeSpan.FromSeconds(Constants.MinKeepAliveTimeout * 2).TotalMilliseconds);

            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task ABP_From_Another_Gateway_Unconfirmed_Message_Should_Load_Device_Cache_And_Disconnect()
        {
            const string gateway1        = "gateway1";
            const string gateway2        = "gateway2";
            var          simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: gateway1))
            {
                FrmCntUp = 9
            };

            LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simulatedDevice.DevAddr, simulatedDevice.DevEUI, "1234")
            {
                GatewayId = gateway2
            }.AsList()));

            using var cache          = NewMemoryCache();
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var payload1 = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 10);

            using var request1 = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload1);
            messageProcessor.DispatchRequest(request1);
            Assert.True(await request1.WaitCompleteAsync());
            Assert.True(request1.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request1.ProcessingFailedReason);

            // Let loading finish
            await Task.Delay(50);

            // device should be cached
            Assert.True(DeviceCache.TryGetByDevEui(simulatedDevice.DevEUI, out var cachedDevice));

            var payload2 = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 11);

            using var request2 = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload2);
            messageProcessor.DispatchRequest(request2);
            Assert.True(await request2.WaitCompleteAsync());
            Assert.True(request2.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request2.ProcessingFailedReason);

            // Expectations
            // 1. we never loaded the twins for the device not belonging to us
            LoRaDeviceClient.Verify(x => x.GetTwinAsync(It.IsAny <CancellationToken>()), Times.Never);
            LoRaDeviceClient.Verify(x => x.EnsureConnected(), Times.Never);

            LoRaDeviceApi.VerifyAll();
            LoRaDeviceApi.Verify(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value), Times.Once());

            // 2. The device is registered
            Assert.Equal(1, DeviceCache.RegistrationCount(simulatedDevice.DevAddr.Value));
        }
Example #6
0
        public async Task When_Fcnt_Down_Fails_Should_Stop_And_Not_Update_Device_Twin(uint initialFcntDown, uint initialFcntUp, uint payloadFcnt)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: null))
            {
                FrmCntDown = initialFcntDown,
                FrmCntUp   = initialFcntUp
            };

            var devEui  = simulatedDevice.LoRaDevice.DevEui;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr;

            LoRaDeviceApi
            .Setup(x => x.ExecuteFunctionBundlerAsync(devEui, It.IsAny <FunctionBundlerRequest>()))
            .ReturnsAsync(() =>
                          new FunctionBundlerResult
            {
                AdrResult = new LoRaTools.ADR.LoRaADRResult {
                    CanConfirmToDevice = true, FCntDown = 0
                },
                NextFCntDown = 0
            });

            LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(devEui, It.IsAny <uint>(), It.IsNotNull <string>()))
            .ReturnsAsync(true);

            using var memoryCache = new MemoryCache(new MemoryCacheOptions());
            var device = CreateLoRaDevice(simulatedDevice);

            DeviceCache.Register(device);
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("hello", fcnt: payloadFcnt);

            using var request = CreateWaitableRequest(unconfirmedMessagePayload);
            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.Null(request.ResponseDownlink);

            // verify that the device in device registry has correct properties and frame counters
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, out var loRaDevice));
            Assert.Equal(devAddr, loRaDevice.DevAddr);
            Assert.Equal(devEui, loRaDevice.DevEUI);
            Assert.True(loRaDevice.IsABP);
            Assert.Equal(initialFcntUp, loRaDevice.FCntUp);
            Assert.Equal(initialFcntDown, loRaDevice.FCntDown);
            Assert.False(loRaDevice.HasFrameCountChanges);

            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task OTAA_Unconfirmed_Message_With_Fcnt_Change_Of_10_Should_Send_Data_To_IotHub_Update_FcntUp_And_Return_Null()
        {
            const uint PayloadFcnt           = 19;
            const uint InitialDeviceFcntUp   = 9;
            const uint InitialDeviceFcntDown = 20;

            var simulatedDevice = new SimulatedDevice(
                TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID),
                frmCntUp: InitialDeviceFcntUp,
                frmCntDown: InitialDeviceFcntDown);

            var loraDevice = CreateLoRaDevice(simulatedDevice);

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true);

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

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loraDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

            // Expectations
            // 1. Message was sent to IoT Hub
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();

            // 2. Return nothing
            Assert.Null(request.ResponseDownlink);
            Assert.True(request.ProcessingSucceeded);

            // 4. Frame counter up was updated
            Assert.Equal(PayloadFcnt, loraDevice.FCntUp);

            // 5. Frame counter down is not changed
            Assert.Equal(InitialDeviceFcntDown, loraDevice.FCntDown);

            // 6. Frame count has no pending changes
            Assert.False(loraDevice.HasFrameCountChanges);
        }
Example #8
0
        public async Task OTAA_Unconfirmed_With_Cloud_To_Device_Mac_Command_Fails_Due_To_Wrong_Setup(string mac, int?fport)
        {
            const int PayloadFcnt           = 20;
            const int InitialDeviceFcntUp   = 9;
            const int InitialDeviceFcntDown = 20;

            var simulatedDevice = new SimulatedDevice(
                TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID),
                frmCntUp: InitialDeviceFcntUp,
                frmCntDown: InitialDeviceFcntDown);

            var loraDevice = CreateLoRaDevice(simulatedDevice);

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true);

            var c2dJson = $"{{\"fport\":{fport}, \"payload\":\"asd\", \"macCommands\": [ {{ \"cid\": \"{mac}\" }}] }}";

            using var cloudToDeviceMessage = new Message(Encoding.UTF8.GetBytes(c2dJson));

            LoRaDeviceClient.SetupSequence(x => x.ReceiveAsync(It.IsAny <TimeSpan>()))
            .ReturnsAsync(cloudToDeviceMessage)
            .ReturnsAsync((Message)null);     // 2nd cloud to device message does not return anything

            LoRaDeviceClient.Setup(x => x.RejectAsync(cloudToDeviceMessage))
            .ReturnsAsync(true);

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loraDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt);

            using var request = CreateWaitableRequest(payload, constantElapsedTime: TimeSpan.Zero);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

            // Expectations
            // 1. Message was sent to IoT Hub
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();

            // 2. DownStream Message should be null as the processing should fail
            Assert.Null(request.ResponseDownlink);
        }
Example #9
0
        public async Task After_ClassA_Sends_Data_Should_Disconnect()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1))
            {
                FrmCntUp = 10
            };

            // message will be sent
            LoRaDeviceTelemetry loRaDeviceTelemetry = null;

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => loRaDeviceTelemetry = t)
            .ReturnsAsync(true);

            // C2D message will be checked
            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            // will check client connection
            LoRaDeviceClient.Setup(x => x.EnsureConnected())
            .Returns(true);

            // will disconnected client
            using var disconnectedEvent = new SemaphoreSlim(0, 1);
            LoRaDeviceClient.Setup(x => x.Disconnect())
            .Callback(() => disconnectedEvent.Release())
            .Returns(true);

            var cachedDevice = CreateLoRaDevice(simulatedDevice);

            cachedDevice.KeepAliveTimeout = 3;

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(cachedDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("hello");

            using var request = CreateWaitableRequest(unconfirmedMessagePayload);
            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingSucceeded);

            await EnsureDisconnectedAsync(disconnectedEvent);

            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task OTAA_Confirmed_Message_Should_Send_Data_To_IotHub_Update_FcntUp_And_Return_DownstreamMessage()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID));
            var payload         = simulatedDevice.CreateConfirmedDataUpMessage("1234");

            var loraDevice = CreateLoRaDevice(simulatedDevice);

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true);

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loraDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

            // Expectations
            // 1. Message was sent to IoT Hub
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();

            // 3. Return is downstream message
            Assert.NotNull(request.ResponseDownlink);
            Assert.True(request.ProcessingSucceeded);
            Assert.Single(DownstreamMessageSender.DownlinkMessages);
            var downlinkMessage = DownstreamMessageSender.DownlinkMessages.First();
            var payloadDataDown = new LoRaPayloadData(downlinkMessage.Data);

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

            // 4. Frame counter up was updated
            Assert.Equal(1U, loraDevice.FCntUp);

            // 5. Frame counter down was incremented
            Assert.Equal(1U, loraDevice.FCntDown);
            Assert.Equal(1, payloadDataDown.Fcnt);
        }
        public async Task OTAA_Join_Should_Use_Rchf_0(string deviceGatewayID, uint rfch)
        {
            var simulatedDevice    = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequestPayload = simulatedDevice.CreateJoinRequest();

            var devAddr = (DevAddr?)null;
            var devEui  = simulatedDevice.LoRaDevice.DevEui;

            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEui.ToString();
            twin.Properties.Desired[TwinProperty.AppEui]        = simulatedDevice.LoRaDevice.AppEui?.ToString();
            twin.Properties.Desired[TwinProperty.AppKey]        = simulatedDevice.LoRaDevice.AppKey?.ToString();
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(twin);

            // Device twin will be updated
            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true);

            // Lora device api will be search by devices with matching deveui,
            LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequestPayload.DevNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList()));

            // using factory to create mock of

            using var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);
            var radio = TestUtils.GenerateTestRadioMetadata(antennaPreference: rfch);

            using var joinRequest = CreateWaitableRequest(radio, joinRequestPayload);
            messageProcessor.DispatchRequest(joinRequest);
            Assert.True(await joinRequest.WaitCompleteAsync());
            Assert.True(joinRequest.ProcessingSucceeded);
            Assert.NotNull(joinRequest.ResponseDownlink);
            Assert.Single(DownstreamMessageSender.DownlinkMessages);
            var downlinkJoinAcceptMessage = DownstreamMessageSender.DownlinkMessages[0];

            // validates txpk according to eu region
            Assert.True(RegionManager.EU868.TryGetDownstreamChannelFrequency(radio.Frequency, joinRequest.RadioMetadata.DataRate, deviceJoinInfo: null, downstreamFrequency: out var receivedFrequency));
            Assert.Equal(receivedFrequency, downlinkJoinAcceptMessage.Rx1?.Frequency);

            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task Validate_Function_Bundler_Execution()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1));
            var devEUI          = simulatedDevice.LoRaDevice.DevEui;

            var loRaDevice = CreateLoRaDevice(simulatedDevice);

            loRaDevice.Deduplication = DeduplicationMode.Drop;

            LoRaDeviceApi
            .Setup(x => x.ExecuteFunctionBundlerAsync(devEUI, It.IsAny <FunctionBundlerRequest>()))
            .ReturnsAsync(() => new FunctionBundlerResult
            {
                AdrResult = new LoRaTools.ADR.LoRaADRResult
                {
                    CanConfirmToDevice = true,
                    FCntDown           = simulatedDevice.FrmCntDown + 1,
                },
                DeduplicationResult = new DeduplicationResult(),
                NextFCntDown        = simulatedDevice.FrmCntDown + 1
            });

            LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(It.IsNotNull <DevEui>(), It.IsAny <uint>(), It.IsNotNull <string>()))
            .ReturnsAsync(true);

            LoRaDeviceClient
            .Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

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

            using var cache               = EmptyMemoryCache();
            using var loraDeviceCache     = CreateDeviceCache(loRaDevice);
            using var loRaDeviceRegistry1 = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            using var messageProcessor1 = new MessageDispatcher(
                      ServerConfiguration,
                      loRaDeviceRegistry1,
                      FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateConfirmedDataUpMessage("1234", fcnt: 1);

            using var request = CreateWaitableRequest(payload);

            messageProcessor1.DispatchRequest(request);

            Assert.True(await request.WaitCompleteAsync());

            LoRaDeviceApi.VerifyAll();
        }
        public async Task When_New_ABP_Device_Instance_Is_Created_Should_Increment_FCntDown()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerGatewayID));

            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DevEui, "pk");

            LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(It.IsAny <DevAddr>()))
            .ReturnsAsync(new SearchDevicesResult(iotHubDeviceInfo.AsList()));

            // device will:
            // - be initialized
            // - send event
            // - receive c2d
            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None))
            .ReturnsAsync(simulatedDevice.CreateABPTwin());
            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);
            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            using var cache          = NewMemoryCache();
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 2);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload, constantElapsedTime: TimeSpan.FromMilliseconds(300));
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingSucceeded);

            // Wait until loader updates the device cache
            await Task.Delay(50);

            Assert.Equal(1, DeviceCache.RegistrationCount(simulatedDevice.DevAddr.Value));
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, out var cachedDevice));
            Assert.True(cachedDevice.IsOurDevice);
            Assert.Equal(Constants.MaxFcntUnsavedDelta - 1U, cachedDevice.FCntDown);
            Assert.Equal(payload.Fcnt, (ushort)cachedDevice.FCntUp);

            // Device was searched by DevAddr
            LoRaDeviceApi.VerifyAll();

            // Device was created by factory
            LoRaDeviceClient.VerifyAll();
        }
        public async Task When_ABP_Device_With_Relaxed_FrameCounter_Has_FCntUP_Zero_Or_One_Should_Reset_Counter_And_Process_Message(uint payloadFCnt)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID));

            // generate payload with frame count 0 or 1
            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFCnt);

            simulatedDevice.FrmCntDown = 0;
            simulatedDevice.FrmCntUp   = 10;


            var loraDevice = CreateLoRaDevice(simulatedDevice);

            // Will send the event to IoT Hub
            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

            // will try to get C2D message
            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())).ReturnsAsync((Message)null);

            // Will save the fcnt up/down to zero
            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.Is <TwinCollection>((t) => IsTwinFcntZero(t)), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true);

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loraDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

            // Expectations
            // 1. Message was sent to IoT Hub
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();

            // 3. Return is null (there is nothing to send downstream)
            Assert.Null(request.ResponseDownlink);
            Assert.True(request.ProcessingSucceeded);

            // 4. Frame counter up was updated
            Assert.Equal(payloadFCnt, loraDevice.FCntUp);
        }
        public async Task When_Getting_DLSettings_From_Twin_Returns_JoinAccept_With_Correct_Settings(int rx1DROffset, DataRateIndex rx2datarate)
        {
            var deviceGatewayID = ServerGatewayID;
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequest     = simulatedDevice.CreateJoinRequest();

            var devAddr = (DevAddr?)null;
            var devEui  = simulatedDevice.LoRaDevice.DevEui;

            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEui.ToString();
            twin.Properties.Desired[TwinProperty.AppEui]        = simulatedDevice.LoRaDevice.AppEui?.ToString();
            twin.Properties.Desired[TwinProperty.AppKey]        = simulatedDevice.LoRaDevice.AppKey?.ToString();
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            twin.Properties.Desired[TwinProperty.RX1DROffset]   = rx1DROffset;
            twin.Properties.Desired[TwinProperty.RX2DataRate]   = rx2datarate;

            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(twin);

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true);

            // Lora device api will be search by devices with matching deveui,
            LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequest.DevNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList()));

            using var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.NotNull(request.ResponseDownlink);
            Assert.Single(DownstreamMessageSender.DownlinkMessages);
            var downlinkMessage = DownstreamMessageSender.DownlinkMessages[0];
            var joinAccept      = new LoRaPayloadJoinAccept(downlinkMessage.Data, simulatedDevice.LoRaDevice.AppKey.Value);

            Assert.Equal(rx1DROffset, joinAccept.Rx1DrOffset);
            Assert.Equal(rx2datarate, joinAccept.Rx2Dr);
        }
Example #16
0
        public async Task When_Device_With_Downlink_Disabled_Received_Unconfirmed_Data_Should_Not_Check_For_C2D(string deviceGatewayID)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID));

            // message will be sent
            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);
            if (string.IsNullOrEmpty(deviceGatewayID))
            {
                LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(It.IsNotNull <DevEui>(), It.IsAny <uint>(), It.IsNotNull <string>()))
                .ReturnsAsync(true);
            }

            using var memoryCache = new MemoryCache(new MemoryCacheOptions());
            var cachedDevice = CreateLoRaDevice(simulatedDevice);

            cachedDevice.DownlinkEnabled = false;

            DeviceCache.Register(cachedDevice);

            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234");

            using var request = CreateWaitableRequest(unconfirmedMessagePayload);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.Null(request.ResponseDownlink);
            Assert.True(request.ProcessingSucceeded);

            LoRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsAny <TimeSpan>()), Times.Never());

            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task When_Device_AppEUI_Does_Not_Match_Should_Return_Null()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: ServerConfiguration.GatewayID));
            var joinRequest     = simulatedDevice.CreateJoinRequest();

            var devEui = simulatedDevice.LoRaDevice.DevEui;

            simulatedDevice.LoRaDevice.AppEui = new JoinEui(0xFFFFFFFFFFFFFFFF);

            using var connectionManager = new SingleDeviceConnectionManager(LoRaDeviceClient.Object);
            using var loRaDevice        = TestUtils.CreateFromSimulatedDevice(simulatedDevice, connectionManager);
            loRaDevice.SetFcntDown(10);
            loRaDevice.SetFcntUp(20);

            var loRaDeviceRegistryMock = new Mock <ILoRaDeviceRegistry>(MockBehavior.Strict);

            loRaDeviceRegistryMock.Setup(x => x.RegisterDeviceInitializer(It.IsNotNull <ILoRaDeviceInitializer>()));
            loRaDeviceRegistryMock.Setup(x => x.GetDeviceForJoinRequestAsync(devEui, joinRequest.DevNonce))
            .ReturnsAsync(() => loRaDevice);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      loRaDeviceRegistryMock.Object,
                      FrameCounterUpdateStrategyProvider);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.Null(request.ResponseDownlink);

            // Device frame counts did not changed
            Assert.Equal(10U, loRaDevice.FCntDown);
            Assert.Equal(20U, loRaDevice.FCntUp);

            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
            loRaDeviceRegistryMock.VerifyAll();

            loRaDeviceRegistryMock.Setup(dr => dr.Dispose());
            LoRaDeviceClient.Setup(ldc => ldc.Dispose());
        }
        public async Task When_Mic_Check_Fails_Join_Process_Should_Fail()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: ServerConfiguration.GatewayID));
            var wrongAppKey     = TestKeys.CreateAppKey(0x3000000, 0x30000);

            var joinRequest = simulatedDevice.CreateJoinRequest(wrongAppKey);

            using var loRaDevice = CreateLoRaDevice(simulatedDevice);
            loRaDevice.SetFcntDown(10);
            loRaDevice.SetFcntUp(20);

            var devEui = simulatedDevice.LoRaDevice.DevEui;

            var loRaDeviceRegistryMock = new Mock <ILoRaDeviceRegistry>(MockBehavior.Strict);

            loRaDeviceRegistryMock.Setup(x => x.RegisterDeviceInitializer(It.IsNotNull <ILoRaDeviceInitializer>()));
            loRaDeviceRegistryMock.Setup(x => x.GetDeviceForJoinRequestAsync(devEui, joinRequest.DevNonce))
            .ReturnsAsync(() => loRaDevice);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      loRaDeviceRegistryMock.Object,
                      FrameCounterUpdateStrategyProvider);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.Null(request.ResponseDownlink);

            // Device frame counts were not modified
            Assert.Equal(10U, loRaDevice.FCntDown);
            Assert.Equal(20U, loRaDevice.FCntUp);

            // Twin property were updated
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
            loRaDeviceRegistryMock.VerifyAll();

            loRaDeviceRegistryMock.Setup(dr => dr.Dispose());
            LoRaDeviceClient.Setup(ldc => ldc.Dispose());
        }
        public async Task Join_Device_Has_Mismatching_AppKey_Should_Return_Null(string deviceGatewayID)
        {
            var simulatedDevice    = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequestPayload = simulatedDevice.CreateJoinRequest();

            var devAddr = (DevAddr?)null;
            var devEui  = simulatedDevice.LoRaDevice.DevEui;

            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEui.ToString();
            twin.Properties.Desired[TwinProperty.AppEui]        = simulatedDevice.LoRaDevice.AppEui?.ToString();
            twin.Properties.Desired[TwinProperty.AppKey]        = "012345678901234567890123456789FF";
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(twin);

            // Lora device api will be search by devices with matching deveui,
            LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequestPayload.DevNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList()));

            // using factory to create mock of
            using var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            // join request should fail
            using var joinRequest = CreateWaitableRequest(joinRequestPayload);
            messageProcessor.DispatchRequest(joinRequest);
            Assert.True(await joinRequest.WaitCompleteAsync());
            Assert.True(joinRequest.ProcessingFailed);
            Assert.Null(joinRequest.ResponseDownlink);

            LoRaDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Never);
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task When_Api_Takes_Too_Long_Should_Return_Null()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: ServerConfiguration.GatewayID));
            var joinRequest     = simulatedDevice.CreateJoinRequest();

            using var loRaDevice = CreateLoRaDevice(simulatedDevice);
            loRaDevice.SetFcntDown(10);
            loRaDevice.SetFcntUp(20);

            var devEui = simulatedDevice.LoRaDevice.DevEui;

            var loRaDeviceRegistryMock = new Mock <ILoRaDeviceRegistry>(MockBehavior.Loose);

            loRaDeviceRegistryMock.Setup(x => x.RegisterDeviceInitializer(It.IsNotNull <ILoRaDeviceInitializer>()));
            loRaDeviceRegistryMock.Setup(x => x.GetDeviceForJoinRequestAsync(devEui, joinRequest.DevNonce))
            .ReturnsAsync(loRaDevice);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      loRaDeviceRegistryMock.Object,
                      FrameCounterUpdateStrategyProvider);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest, constantElapsedTime: TimeSpan.FromSeconds(7));
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.Null(request.ResponseDownlink);
            Assert.Empty(DownstreamMessageSender.DownlinkMessages);
            Assert.Equal(LoRaDeviceRequestFailedReason.ReceiveWindowMissed, request.ProcessingFailedReason);

            // Device frame counts were not modified
            Assert.Equal(10U, loRaDevice.FCntDown);
            Assert.Equal(20U, loRaDevice.FCntUp);

            // Twin property were updated
            LoRaDeviceClient.VerifyAll();
            loRaDeviceRegistryMock.VerifyAll();
            LoRaDeviceApi.VerifyAll();

            LoRaDeviceClient.Setup(ldc => ldc.Dispose());
        }
        public async Task ABP_Unconfirmed_Message_Should_Send_Data_To_IotHub_Update_FcntUp_And_Return_Null()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID));
            var payload         = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: 10);

            simulatedDevice.FrmCntUp = 9;

            var loraDevice = CreateLoRaDevice(simulatedDevice);

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loraDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

            // Expectations
            // 1. Message was sent to IoT Hub
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
            Assert.True(request.ProcessingSucceeded);

            // 2. Return is null (there is nothing to send downstream)
            Assert.Null(request.ResponseDownlink);

            // 3. Frame counter up was updated
            Assert.Equal(10U, loraDevice.FCntUp);
        }
Example #22
0
        public DwellTimeIntegrationTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
        {
            this.testOutputLoggerFactory = new TestOutputLoggerFactory(testOutputHelper);
            this.dataRequestHandlerMock  = new Mock <TestDefaultLoRaRequestHandler>(MockBehavior.Default,
                                                                                    ServerConfiguration,
                                                                                    FrameCounterUpdateStrategyProvider,
                                                                                    ConcentratorDeduplication,
                                                                                    PayloadDecoder,
                                                                                    new DeduplicationStrategyFactory(this.testOutputLoggerFactory, this.testOutputLoggerFactory.CreateLogger <DeduplicationStrategyFactory>()),
                                                                                    new LoRaADRStrategyProvider(this.testOutputLoggerFactory),
                                                                                    new LoRAADRManagerFactory(LoRaDeviceApi.Object, this.testOutputLoggerFactory),
                                                                                    new FunctionBundlerProvider(LoRaDeviceApi.Object, this.testOutputLoggerFactory, this.testOutputLoggerFactory.CreateLogger <FunctionBundlerProvider>()),
                                                                                    testOutputHelper)
            {
                CallBase = true
            };

            this.simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(0));
            this.loRaDevice      = CreateLoRaDevice(this.simulatedDevice, registerConnection: false);

            LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(It.IsAny <DevEui>(), It.IsAny <uint>(), It.IsNotNull <string>()))
            .ReturnsAsync(true);
        }
        public async Task Unknown_Device_Should_Not_Send_Messages(int searchDevicesDelayMs)
        {
            // Setup
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1));
            var payload         = simulatedDevice.CreateUnconfirmedDataUpMessage("1234");

            if (searchDevicesDelayMs > 0)
            {
                LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value))
                .Returns(Task.Delay(searchDevicesDelayMs).ContinueWith((_) => new SearchDevicesResult(), TaskScheduler.Default));
            }
            else
            {
                LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value))
                .ReturnsAsync(new SearchDevicesResult());
            }

            using var cache          = NewMemoryCache();
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

            // Expectations
            // 1. Returns null
            Assert.Null(request.ResponseDownlink);
            Assert.True(request.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.NotMatchingDeviceByDevAddr, request.ProcessingFailedReason);
        }
Example #24
0
        public async Task ValidateFcnt_Start_Values_And_ResetCounter(
            short fcntUp,
            uint startFcntUpDesired,
            uint startFcntDownDesired,
            uint?startFcntUpReported,
            uint?startFcntDownReported,
            int?fcntResetCounterDesired,
            int?fcntResetCounterReported,
            uint startUpExpected,
            uint startDownExpected,
            bool saveExpected)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID));

            var devEui  = simulatedDevice.LoRaDevice.DevEui;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr.Value;

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)).ReturnsAsync(true);

            var initialTwin = SetupTwins((uint)fcntUp, startFcntDownDesired, startFcntUpDesired, startFcntDownDesired, true, false, simulatedDevice, devEui, devAddr);

            if (startFcntUpReported.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntUpStart] = startFcntUpReported.Value;
            }
            if (startFcntDownReported.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntDownStart] = startFcntDownReported.Value;
            }

            if (fcntResetCounterDesired.HasValue)
            {
                initialTwin.Properties.Desired[TwinProperty.FCntResetCounter] = fcntResetCounterDesired.Value;
            }
            if (fcntResetCounterReported.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntResetCounter] = fcntResetCounterReported.Value;
            }

            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(initialTwin);

            uint?fcntUpSavedInTwin        = null;
            uint?fcntDownSavedInTwin      = null;
            uint?fcntStartUpSavedInTwin   = null;
            uint?fcntStartDownSavedInTwin = null;

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .Returns <TwinCollection, CancellationToken>((t, _) =>
            {
                fcntUpSavedInTwin        = (uint)t[TwinProperty.FCntUp];
                fcntDownSavedInTwin      = (uint)t[TwinProperty.FCntDown];
                fcntStartUpSavedInTwin   = (uint)t[TwinProperty.FCntUpStart];
                fcntStartDownSavedInTwin = (uint)t[TwinProperty.FCntDownStart];

                return(Task.FromResult(true));
            });

            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())).ReturnsAsync((Message)null);
            LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(devAddr)).ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "abc").AsList()));

            using var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: (uint)fcntUp);

            using var req = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload);

            messageDispatcher.DispatchRequest(req);
            await req.WaitCompleteAsync();

            if (saveExpected)
            {
                Assert.True(req.ProcessingSucceeded);
                LoRaDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Exactly(1));
                Assert.Equal(fcntUpSavedInTwin, fcntStartUpSavedInTwin);
                Assert.Equal(fcntDownSavedInTwin, fcntStartDownSavedInTwin);
                Assert.Equal(startUpExpected, fcntStartUpSavedInTwin.Value);
                Assert.Equal(startDownExpected, fcntStartDownSavedInTwin.Value);
            }
            else
            {
                LoRaDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Never());
            }
        }
Example #25
0
        public async Task Validate_Limits(
            uint payloadFcntUp,
            uint?deviceFcntUp,
            uint?deviceFcntDown,
            uint?startFcntUp,
            uint?startFcntDown,
            uint expectedFcntUp,
            uint expectedFcntDown,
            bool abpRelaxed,
            bool confirmed,
            bool supports32Bit = false,
            LoRaDeviceRequestFailedReason?failedReason = null)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID, supports32BitFcnt: supports32Bit));

            var devEui  = simulatedDevice.LoRaDevice.DevEui;
            var devAddr = simulatedDevice.LoRaDevice.DevAddr.Value;

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)).ReturnsAsync(true);
            var initialTwin = SetupTwins(deviceFcntUp, deviceFcntDown, startFcntUp, startFcntDown, abpRelaxed, supports32Bit, simulatedDevice, devEui, devAddr);

            LoRaDeviceClient
            .Setup(x => x.GetTwinAsync(CancellationToken.None)).Returns(() =>
            {
                return(Task.FromResult(initialTwin));
            });

            uint?fcntUpSavedInTwin   = null;
            uint?fcntDownSavedInTwin = null;

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .Returns <TwinCollection, CancellationToken>((t, _) =>
            {
                fcntUpSavedInTwin   = (uint)t[TwinProperty.FCntUp];
                fcntDownSavedInTwin = (uint)t[TwinProperty.FCntDown];
                return(Task.FromResult(true));
            });

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

            var shouldReset = payloadFcntUp == 0 && abpRelaxed;

            if (shouldReset)
            {
                LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(devEui, It.IsAny <uint>(), It.IsNotNull <string>())).ReturnsAsync(true);
            }

            LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(devAddr))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "abc").AsList()));

            using var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            // Send to message processor
            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var loRaPayloadData = confirmed
                ? simulatedDevice.CreateConfirmedDataUpMessage("1234", fcnt: payloadFcntUp, appSKey: simulatedDevice.AppSKey, nwkSKey: simulatedDevice.NwkSKey)
                : simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcntUp);

            using var req = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), loRaPayloadData);

            messageDispatcher.DispatchRequest(req);
            Assert.True(await req.WaitCompleteAsync(-1));

            if (failedReason.HasValue)
            {
                Assert.Equal(failedReason.Value, req.ProcessingFailedReason);
            }
            else
            {
                Assert.True(req.ProcessingSucceeded);
                Assert.True(DeviceCache.TryGetByDevEui(devEui, out var loRaDevice));

                if (confirmed)
                {
                    Assert.NotNull(req.ResponseDownlink);
                    Assert.True(req.ProcessingSucceeded);
                    Assert.Single(DownstreamMessageSender.DownlinkMessages);
                    var downlinkMessage = DownstreamMessageSender.DownlinkMessages[0];
                    var payloadDataDown = new LoRaPayloadData(downlinkMessage.Data);
                    payloadDataDown.Serialize(simulatedDevice.AppSKey.Value);
                    Assert.Equal(expectedFcntDown, payloadDataDown.Fcnt);
                }

                Assert.Equal(expectedFcntUp, loRaDevice.FCntUp);
            }
        }
        public async Task When_ABP_Sends_Upstream_Followed_By_DirectMethod_Should_Send_Upstream_And_Downstream(string deviceGatewayID, uint fcntDownFromTwin, uint fcntDelta, Region region)
        {
            const uint payloadFcnt = 2; // to avoid relax mode reset

            var simDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID, deviceClassType: 'c'), frmCntDown: fcntDownFromTwin);

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true);

            var twin = simDevice.CreateABPTwin(reportedProperties: new Dictionary <string, object>
            {
                { TwinProperty.Region, region.LoRaRegion.ToString() }
            });

            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None))
            .ReturnsAsync(twin);

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simDevice.DevAddr.Value))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simDevice.DevAddr, simDevice.DevEUI, "123").AsList()));

            if (deviceGatewayID == null)
            {
                LoRaDeviceApi.Setup(x => x.ExecuteFunctionBundlerAsync(simDevice.DevEUI, It.IsNotNull <FunctionBundlerRequest>()))
                .ReturnsAsync(new FunctionBundlerResult());
            }

            using var cache          = NewMemoryCache();
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);
            var payloadData = simDevice.CreateUnconfirmedDataUpMessage("1", fcnt: payloadFcnt);

            using var request = CreateWaitableRequest(payloadData, region: region);
            request.SetStationEui(new StationEui(ulong.MaxValue));
            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingSucceeded);

            // wait until cache has been updated
            await Task.Delay(50);

            // Adds fcntdown to device, simulating multiple downstream calls
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, out var loRaDevice));
            loRaDevice.SetFcntDown(fcntDelta + loRaDevice.FCntDown);

            var classCSender = new DefaultClassCDevicesMessageSender(
                ServerConfiguration,
                deviceRegistry,
                DownstreamMessageSender,
                FrameCounterUpdateStrategyProvider,
                new TestOutputLogger <DefaultClassCDevicesMessageSender>(this.testOutputHelper),
                TestMeter.Instance);

            var c2d = new ReceivedLoRaCloudToDeviceMessage()
            {
                DevEUI    = simDevice.DevEUI,
                MessageId = Guid.NewGuid().ToString(),
                Payload   = "aaaa",
                Fport     = FramePorts.App18,
            };

            var expectedFcntDown = fcntDownFromTwin + Constants.MaxFcntUnsavedDelta + fcntDelta;

            if (string.IsNullOrEmpty(deviceGatewayID))
            {
                LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(simDevice.DevEUI, fcntDownFromTwin + fcntDelta, 0, ServerConfiguration.GatewayID))
                .ReturnsAsync((ushort)expectedFcntDown);
            }

            Assert.True(await classCSender.SendAsync(c2d));
            Assert.Single(DownstreamMessageSender.DownlinkMessages);
            var downstreamMsg = DownstreamMessageSender.DownlinkMessages[0];

            var downstreamPayloadBytes = downstreamMsg.Data;
            var downstreamPayload      = new LoRaPayloadData(downstreamPayloadBytes);

            Assert.Equal(expectedFcntDown, downstreamPayload.Fcnt);
            Assert.Equal(c2d.Fport, downstreamPayload.Fport);
            Assert.Equal(downstreamPayload.DevAddr, simDevice.DevAddr);
            var decryptedPayload = downstreamPayload.GetDecryptedPayload(simDevice.AppSKey.Value);

            Assert.Equal(c2d.Payload, Encoding.UTF8.GetString(decryptedPayload));

            Assert.Equal(expectedFcntDown, loRaDevice.FCntDown);
            Assert.Equal(payloadFcnt, loRaDevice.FCntUp);

            LoRaDeviceApi.VerifyAll();
            LoRaDeviceClient.VerifyAll();
        }
        public async Task When_Updating_PreferredGateway_And_FcntUp_Should_Save_Twin_Once()
        {
            const uint PayloadFcnt           = 10;
            const uint InitialDeviceFcntUp   = 9;
            const uint InitialDeviceFcntDown = 20;

            var simulatedDevice = new SimulatedDevice(
                TestDeviceInfo.CreateABPDevice(1, deviceClassType: 'c'),
                frmCntUp: InitialDeviceFcntUp,
                frmCntDown: InitialDeviceFcntDown);

            var loraDevice = CreateLoRaDevice(simulatedDevice);

            loraDevice.UpdatePreferredGatewayID("another-gateway", acceptChanges: true);

            var bundlerResult = new FunctionBundlerResult()
            {
                PreferredGatewayResult = new PreferredGatewayResult()
                {
                    DevEUI             = simulatedDevice.DevEUI,
                    PreferredGatewayID = ServerGatewayID,
                    CurrentFcntUp      = PayloadFcnt,
                    RequestFcntUp      = PayloadFcnt,
                }
            };

            LoRaDeviceApi
            .Setup(x => x.ExecuteFunctionBundlerAsync(simulatedDevice.DevEUI, It.Is((FunctionBundlerRequest r) => PayloadFcnt == r.ClientFCntUp &&
                                                                                    ServerGatewayID == r.GatewayId &&
                                                                                    FunctionBundlerItemType.PreferredGateway == r.FunctionItems)))
            .ReturnsAsync(bundlerResult);

            TwinCollection actualSavedTwin = null;

            LoRaDeviceClient
            .Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .Callback <TwinCollection, CancellationToken>((savedTwin, _) => actualSavedTwin = savedTwin)
            .ReturnsAsync(true);

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

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

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loraDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt);

            using var request = CreateWaitableRequest(payload);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

            // Expectations
            // 1. Message was sent to IoT Hub
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();

            // 2. No downstream message for the current device is sent
            Assert.Null(request.ResponseDownlink);
            Assert.True(request.ProcessingSucceeded);

            Assert.Equal(ServerGatewayID, loraDevice.PreferredGatewayID);
            Assert.Equal(LoRaRegionType.EU868, loraDevice.LoRaRegion);
            Assert.Equal(PayloadFcnt, loraDevice.FCntUp);

            LoRaDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Once());

            Assert.Equal(ServerGatewayID, actualSavedTwin[TwinProperty.PreferredGatewayID].Value as string);
            Assert.Equal(LoRaRegionType.EU868.ToString(), actualSavedTwin[TwinProperty.Region].Value as string);
            Assert.Equal(PayloadFcnt, (uint)actualSavedTwin[TwinProperty.FCntUp].Value);
        }
        public async Task When_Processing_Data_Request_Should_Compute_Preferred_Gateway_And_Region(
            [CombinatorialValues(null, ServerGatewayID)] string deviceGatewayID,
            [CombinatorialValues(null, ServerGatewayID, "another-gateway")] string initialPreferredGatewayID,
            [CombinatorialValues(ServerGatewayID, "another-gateway")] string preferredGatewayID,
            [CombinatorialValues(null, LoRaRegionType.EU868, LoRaRegionType.US915)] LoRaRegionType?initialLoRaRegion)
        {
            const uint PayloadFcnt           = 10;
            const uint InitialDeviceFcntUp   = 9;
            const uint InitialDeviceFcntDown = 20;

            var simulatedDevice = new SimulatedDevice(
                TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID, deviceClassType: 'c'),
                frmCntUp: InitialDeviceFcntUp,
                frmCntDown: InitialDeviceFcntDown);

            var loraDevice = CreateLoRaDevice(simulatedDevice);

            loraDevice.UpdatePreferredGatewayID(initialPreferredGatewayID, acceptChanges: true);
            if (initialLoRaRegion.HasValue)
            {
                loraDevice.UpdateRegion(initialLoRaRegion.Value, acceptChanges: true);
            }

            var shouldSavePreferredGateway = string.IsNullOrEmpty(deviceGatewayID) && initialPreferredGatewayID != preferredGatewayID && preferredGatewayID == ServerGatewayID;
            var shouldSaveRegion           = (!initialLoRaRegion.HasValue || initialLoRaRegion.Value != LoRaRegionType.EU868) && (preferredGatewayID == ServerGatewayID || deviceGatewayID != null);

            var bundlerResult = new FunctionBundlerResult()
            {
                PreferredGatewayResult = new PreferredGatewayResult()
                {
                    DevEUI             = simulatedDevice.DevEUI,
                    PreferredGatewayID = preferredGatewayID,
                    CurrentFcntUp      = PayloadFcnt,
                    RequestFcntUp      = PayloadFcnt,
                }
            };

            if (string.IsNullOrEmpty(deviceGatewayID))
            {
                LoRaDeviceApi.Setup(x =>
                                    x.ExecuteFunctionBundlerAsync(simulatedDevice.DevEUI,
                                                                  It.Is((FunctionBundlerRequest r) => r.ClientFCntUp == PayloadFcnt &&
                                                                        r.GatewayId == ServerGatewayID &&
                                                                        r.FunctionItems == FunctionBundlerItemType.PreferredGateway)))
                .ReturnsAsync(bundlerResult);
            }

            TwinCollection actualSavedTwin = null;

            if (shouldSavePreferredGateway || shouldSaveRegion)
            {
                LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
                .ReturnsAsync(true)
                .Callback((TwinCollection t, CancellationToken _) => actualSavedTwin = t);
            }

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

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

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loraDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt);

            using var request = CreateWaitableRequest(payload, constantElapsedTime: TimeSpan.Zero);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

            // Expectations
            // 1. Message was sent to IoT Hub
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();

            // 2. No downstream message for the current device is sent
            Assert.Null(request.ResponseDownlink);
            Assert.True(request.ProcessingSucceeded);

            if (!string.IsNullOrEmpty(deviceGatewayID))
            {
                Assert.Equal(initialPreferredGatewayID, loraDevice.PreferredGatewayID);
            }
            else
            {
                Assert.Equal(preferredGatewayID, loraDevice.PreferredGatewayID);
            }

            Assert.Equal(LoRaRegionType.EU868, loraDevice.LoRaRegion);

            if (shouldSavePreferredGateway || shouldSaveRegion)
            {
                if (shouldSavePreferredGateway)
                {
                    Assert.Equal(ServerGatewayID, actualSavedTwin[TwinProperty.PreferredGatewayID].Value as string);
                }
                else
                {
                    Assert.False(actualSavedTwin.Contains(TwinProperty.PreferredGatewayID));
                }

                if (shouldSaveRegion)
                {
                    Assert.Equal(LoRaRegionType.EU868.ToString(), actualSavedTwin[TwinProperty.Region].Value as string);
                }
                else
                {
                    Assert.False(actualSavedTwin.Contains(TwinProperty.Region));
                }
            }
        }
        public async Task When_Joining_Should_Save_Region_And_Preferred_Gateway(
            [CombinatorialValues(null, ServerGatewayID)] string deviceGatewayID,
            [CombinatorialValues(null, ServerGatewayID, "another-gateway")] string initialPreferredGatewayID,
            [CombinatorialValues(null, LoRaRegionType.EU868, LoRaRegionType.US915)] LoRaRegionType?initialLoRaRegion)
        {
            var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: deviceGatewayID));

            var customReportedProperties = new Dictionary <string, object>();

            // reported: { 'PreferredGateway': '' } -> if device is for multiple gateways and one initial was defined
            if (string.IsNullOrEmpty(deviceGatewayID) && !string.IsNullOrEmpty(initialPreferredGatewayID))
            {
                customReportedProperties[TwinProperty.PreferredGatewayID] = initialPreferredGatewayID;
            }

            if (initialLoRaRegion.HasValue)
            {
                customReportedProperties[TwinProperty.Region] = initialLoRaRegion.Value.ToString();
            }

            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None))
            .ReturnsAsync(simDevice.CreateOTAATwin(reportedProperties: customReportedProperties));

            var shouldSavePreferredGateway = string.IsNullOrEmpty(deviceGatewayID) && initialPreferredGatewayID != ServerGatewayID;
            var shouldSaveRegion           = !initialLoRaRegion.HasValue || initialLoRaRegion.Value != LoRaRegionType.EU868;

            var            savedAppSKey         = string.Empty;
            var            savedNwkSKey         = string.Empty;
            var            savedDevAddr         = string.Empty;
            TwinCollection actualTwinCollection = null;

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true)
            .Callback <TwinCollection, CancellationToken>((t, _) =>
            {
                savedAppSKey         = t[TwinProperty.AppSKey];
                savedNwkSKey         = t[TwinProperty.NwkSKey];
                savedDevAddr         = t[TwinProperty.DevAddr];
                actualTwinCollection = t;
            });

            LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, simDevice.DevEUI, It.IsAny <DevNonce>()))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(simDevice.DevAddr, simDevice.DevEUI, "123").AsList()));

            using var cache          = NewMemoryCache();
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var joinPayload = simDevice.CreateJoinRequest();

            using var joinRequest = CreateWaitableRequest(joinPayload);
            messageDispatcher.DispatchRequest(joinRequest);
            Assert.True(await joinRequest.WaitCompleteAsync());
            Assert.True(joinRequest.ProcessingSucceeded);

            Assert.NotEmpty(savedAppSKey);
            Assert.NotEmpty(savedNwkSKey);
            Assert.NotEmpty(savedDevAddr);
            if (shouldSaveRegion)
            {
                Assert.Equal(LoRaRegionType.EU868.ToString(), actualTwinCollection[TwinProperty.Region].Value as string);
            }
            else
            {
                Assert.False(actualTwinCollection.Contains(TwinProperty.Region));
            }

            // Only save preferred gateway if device does not have one assigned
            if (shouldSavePreferredGateway)
            {
                Assert.Equal(ServerConfiguration.GatewayID, actualTwinCollection[TwinProperty.PreferredGatewayID].Value as string);
            }
            else
            {
                Assert.False(actualTwinCollection.Contains(TwinProperty.PreferredGatewayID));
            }

            Assert.True(DeviceCache.TryGetByDevEui(simDevice.DevEUI, out var loRaDevice));

            Assert.Equal(LoRaDeviceClassType.C, loRaDevice.ClassType);
            if (string.IsNullOrEmpty(simDevice.LoRaDevice.GatewayID))
            {
                Assert.Equal(ServerConfiguration.GatewayID, loRaDevice.PreferredGatewayID);
            }
            else
            {
                Assert.Empty(loRaDevice.PreferredGatewayID);
            }

            Assert.Equal(LoRaRegionType.EU868, loRaDevice.LoRaRegion);

            LoRaDeviceApi.VerifyAll();
            LoRaDeviceClient.VerifyAll();
        }
        public async Task Unconfirmed_Cloud_To_Device_From_Decoder_Should_Call_ClassC_Message_Sender()
        {
            const uint PayloadFcnt           = 10;
            const uint InitialDeviceFcntUp   = 9;
            const uint InitialDeviceFcntDown = 20;
            var        devEui = new DevEui(2);

            var simulatedDevice = new SimulatedDevice(
                TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID),
                frmCntUp: InitialDeviceFcntUp,
                frmCntDown: InitialDeviceFcntDown);

            var loraDevice = CreateLoRaDevice(simulatedDevice);

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .ReturnsAsync(true);

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

            var decoderResult = new DecodePayloadResult("1")
            {
                CloudToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage()
                {
                    Fport     = FramePorts.App1,
                    MessageId = "123",
                    Payload   = "12",
                    DevEUI    = devEui,
                },
            };

            var payloadDecoder = new Mock <ILoRaPayloadDecoder>(MockBehavior.Strict);

            payloadDecoder.Setup(x => x.DecodeMessageAsync(simulatedDevice.DevEUI, It.IsNotNull <byte[]>(), FramePorts.App1, It.IsAny <string>()))
            .ReturnsAsync(decoderResult);
            PayloadDecoder.SetDecoder(payloadDecoder.Object);

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loraDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            using var c2dMessageSent = new SemaphoreSlim(0);
            var classCMessageSender = new Mock <IClassCDeviceMessageSender>(MockBehavior.Strict);

            classCMessageSender.Setup(x => x.SendAsync(It.Is <IReceivedLoRaCloudToDeviceMessage>(m => !m.Confirmed && m.DevEUI == devEui), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true)
            .Callback <IReceivedLoRaCloudToDeviceMessage, CancellationToken>((m, _) => c2dMessageSent.Release());
            RequestHandlerImplementation.SetClassCMessageSender(classCMessageSender.Object);

            // Send to message processor
            using var messageProcessor = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt);

            using var request = CreateWaitableRequest(payload);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

            // Expectations
            // 1. Message was sent to IoT Hub
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();

            // 2. No downstream message for the current device is sent
            Assert.Null(request.ResponseDownlink);
            Assert.True(request.ProcessingSucceeded);

            // 4. Frame counter up was updated
            Assert.Equal(PayloadFcnt, loraDevice.FCntUp);

            // 5. Frame counter down is unchanged
            Assert.Equal(InitialDeviceFcntDown, loraDevice.FCntDown);

            // 6. Frame count has pending changes
            Assert.True(loraDevice.HasFrameCountChanges);

            // Ensure the message was sent
            Assert.True(await c2dMessageSent.WaitAsync(10 * 1000));

            payloadDecoder.VerifyAll();
            classCMessageSender.VerifyAll();
        }