Exemple #1
0
    public void StartAutoExportTxt()
    {
        tokenSource = new CancellationTokenSource();
        Console.WriteLine("StartAutoExportTxt");

        IQueryable <GroundTruth> groundtruths = null;

        _ExportTask = Task.Factory.StartNew(() =>
        {
            var GVContext = Startup.CreateDBContext();
            while (true)
            {
                groundtruths = GVContext.GroundTruths.OrderBy(item => item.ip);


                foreach (GroundTruth groundtruth in groundtruths)
                {
                    DeviceCache dv = GetDevice(groundtruth.ip);
                    dv.ProcessOnce();
                }

                Thread.Sleep(30000);

                if (tokenSource.IsCancellationRequested == true)
                {
                    Console.WriteLine("Task {0} was cancelled before it got started.",
                                      tokenSource);
                    tokenSource.Token.ThrowIfCancellationRequested();
                }
            }
        });
    }
Exemple #2
0
        public async Task When_Devices_From_Another_Gateway_Is_Cached_Return_Null()
        {
            var simulatedDevice1 = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: "another-gateway"));

            using var loraDevice1   = TestUtils.CreateFromSimulatedDevice(simulatedDevice1, ConnectionManager);
            loraDevice1.IsOurDevice = false;

            DeviceCache.Register(loraDevice1);

            var payload = simulatedDevice1.CreateUnconfirmedDataUpMessage("1234");

            var apiService = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);

            using var target  = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, this.loraDeviceFactoryMock.Object, DeviceCache);
            using var request = WaitableLoRaRequest.Create(payload);
            var queue = target.GetLoRaRequestQueue(request);

            queue.Queue(request);
            Assert.IsType <ExternalGatewayLoRaRequestQueue>(queue);
            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request.ProcessingFailedReason);

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

            // Device was created by factory
            this.loraDeviceFactoryMock.VerifyAll();
        }
Exemple #3
0
        public async Task GetDeviceForJoinRequestAsync_When_Join_Handled_By_Other_Cache_Is_Updated(bool joinedDevice)
        {
            var devNonce   = new DevNonce(1);
            var apiService = new Mock <LoRaDeviceAPIServiceBase>();
            var otaaDevice = TestDeviceInfo.CreateOTAADevice(1);

            if (joinedDevice)
            {
                otaaDevice.AppSKey = new AppSessionKey();
            }

            var simulatedDevice = new SimulatedDevice(otaaDevice);

            apiService.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, simulatedDevice.DevEUI, devNonce))
            .ReturnsAsync(new SearchDevicesResult()
            {
                IsDevNonceAlreadyUsed = true
            });

            DeviceCache.Register(CreateLoRaDevice(simulatedDevice));
            using var target = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, this.loraDeviceFactoryMock.Object, DeviceCache);

            Assert.Null(await target.GetDeviceForJoinRequestAsync(simulatedDevice.DevEUI, devNonce));
            Assert.Equal(joinedDevice, !DeviceCache.TryGetByDevEui(simulatedDevice.DevEUI, out _));
        }
Exemple #4
0
        /// <summary>
        /// 以新增和删除设备为例,使用单例模式和MVP设计模式
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Demo demo = new Demo();
            //获取缓存
            DeviceCache cacheInstance = DeviceCache.GetInstance();
            //创建View实例
            DemoView view = new DemoView();
            //创建四个新设备
            Device device1 = Device.CreatDevice("device_1", new Point(0, 0));
            Device device2 = Device.CreatDevice("device_2", new Point(0, 1));
            Device device3 = Device.CreatDevice("device_3", new Point(1, 0));
            Device device4 = Device.CreatDevice("device_4", new Point(1, 1));

            //添加四个设备
            List <Device> newDevices = new List <Device>()
            {
                device1, device2, device3, device4
            };

            foreach (Device newDevice in newDevices)
            {
                view.NewDeviceAdding(newDevice);
            }
            cacheInstance.ShowCache();

            //删除设备2
            view.DeviceRemoving(device2);
            cacheInstance.ShowCache();

            Console.ReadKey();
        }
Exemple #5
0
        public DemoPresenter(IDemoView view)
        {
            _view      = view;
            _operation = new CacheOperation();
            DeviceCache cacheInstance = DeviceCache.GetInstance();

            cacheInstance.AddAction    += AddDeviceToView;
            cacheInstance.RemoveAction += RemoveDeviceFromView;
        }
        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));
        }
Exemple #7
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();
        }
Exemple #8
0
 public DeviceRepository()
 {
     if (DeviceCache.Count == 0)
     {
         lock (lockObject)
         {
             if (DeviceCache.Count == 0)
             {
                 LoadDevices().ToList().ForEach(x => DeviceCache.TryAdd(x.Name, x));
             }
         }
     }
 }
        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();
        }
Exemple #10
0
        public async Task When_Device_Is_Assigned_To_Another_Gateway_Cache_Locally_And_Return_Null()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: "another-gateway"));

            var apiService       = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);
            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DevEui, "pk")
            {
                GatewayId = "another-gateway",
                NwkSKey   = simulatedDevice.NwkSKey.Value
            };

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

            var deviceFactory = new TestLoRaDeviceFactory(LoRaDeviceClient.Object, DeviceCache, ConnectionManager);

            using var target = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, deviceFactory, DeviceCache);

            // request #1
            var payload1 = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 11);

            using var request1 = WaitableLoRaRequest.Create(payload1);
            target.GetLoRaRequestQueue(request1).Queue(request1);
            Assert.True(await request1.WaitCompleteAsync());
            Assert.True(request1.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request1.ProcessingFailedReason);

            // request #2
            var payload2 = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 12);

            using var request2 = WaitableLoRaRequest.Create(payload2);
            target.GetLoRaRequestQueue(request2).Queue(request2);
            Assert.True(await request2.WaitCompleteAsync());
            Assert.True(request2.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request2.ProcessingFailedReason);

            // Device was searched by DevAddr
            apiService.VerifyAll();
            apiService.Verify(x => x.SearchByDevAddrAsync(It.IsAny <DevAddr>()), Times.Once());

            // Device should not be connected
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceClient.Verify(x => x.GetTwinAsync(CancellationToken.None), Times.Never());
            LoRaDeviceClient.Verify(x => x.Disconnect(), Times.Never());

            // device is in cache
            Assert.True(DeviceCache.TryGetForPayload(request1.Payload, out var loRaDevice));
            Assert.False(loRaDevice.IsOurDevice);
        }
Exemple #11
0
 public DeviceCache GetDevice(string ip)
 {
     if (_DevicesCache.ContainsKey(ip))
     {
         return(_DevicesCache[ip]);
     }
     else
     {
         DeviceCache cache = new DeviceCache();
         cache._IP          = ip;
         cache._LastRevTime = DateTime.Now;
         _DevicesCache.Add(ip, cache);
         return(cache);
     }
 }
Exemple #12
0
        public async Task When_Device_Is_Assigned_To_Another_Gateway_After_No_Connection_Should_Be_Established()
        {
            const string gatewayId       = "another-gateway";
            var          simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: gatewayId));

            var apiService       = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict);
            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DevEui, "pk")
            {
                NwkSKey = simulatedDevice.NwkSKey.Value, GatewayId = gatewayId
            };

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

            var deviceFactory = new TestLoRaDeviceFactory(LoRaDeviceClient.Object, DeviceCache, ConnectionManager);

            using var target = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, deviceFactory, DeviceCache);

            // setup 2 requests - ensure the cache is validated before fetching from the function
            var requests = Enumerable.Range(1, 2).Select((n) =>
            {
                var payload = simulatedDevice.CreateUnconfirmedDataUpMessage(n.ToString(CultureInfo.InvariantCulture), fcnt: (uint)n + 10);
                var request = WaitableLoRaRequest.Create(payload);
                target.GetLoRaRequestQueue(request).Queue(request);
                return(request);
            }
                                                         ).ToList();

            foreach (var request in requests)
            {
                Assert.True(await request.WaitCompleteAsync());
                Assert.True(request.ProcessingFailed);
                Assert.Equal(LoRaDeviceRequestFailedReason.BelongsToAnotherGateway, request.ProcessingFailedReason);
            }

            // Device was searched by DevAddr
            apiService.VerifyAll();
            apiService.Verify(x => x.SearchByDevAddrAsync(It.IsAny <DevAddr>()), Times.Once());

            LoRaDeviceClient.Verify(x => x.GetTwinAsync(CancellationToken.None), Times.Never());

            // device is in cache
            Assert.True(DeviceCache.TryGetForPayload(requests.First().Payload, out var cachedLoRaDevice));
            Assert.False(cachedLoRaDevice.IsOurDevice);
        }
Exemple #13
0
    public void Insert(string ip, byte nodeindex, int timestamp, int timestampms, byte lr)
    {
        DeviceCache dv = GetDevice(ip);


        DateTime dt = DateTime.Now;

        App_GroundTruthData data = new App_GroundTruthData();

        data.createtime  = dt;
        data.device      = ip;
        data.timestamp   = timestamp;
        data.timestampms = timestampms;
        data.leftright   = lr;
        data.nodeindex   = nodeindex;

        dv.AddMiniteCache(data);
    }
Exemple #14
0
    public void Insert(string ip, UInt32 timestamps, UInt32 timestampms, UInt16 rate, UInt16 gain, byte[] bufferdata)
    {
        DeviceCache dv = GetDevice(ip);


        DateTime dt = DateTime.Now;

        App_SensorData data = new App_SensorData();

        data.createtime  = dt;
        data.device      = ip;
        data.gain        = (short)gain;
        data.rate        = (short)rate;
        data.sensorvalue = bufferdata;
        data.timestampms = (int)timestampms;
        data.timestamps  = (int)timestamps;

        dv.AddMiniteCache(data);
    }
Exemple #15
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();
        }
Exemple #16
0
        public async Task When_Device_Is_Not_In_Cache_And_Found_In_Api_Should_Cache_And_Process_Request(string deviceGatewayID)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID));
            var payload         = simulatedDevice.CreateUnconfirmedDataUpMessage("1234");

            var apiService       = new Mock <LoRaDeviceAPIServiceBase>();
            var iotHubDeviceInfo = new IoTHubDeviceInfo(simulatedDevice.LoRaDevice.DevAddr, simulatedDevice.LoRaDevice.DevEui, "pk")
            {
                GatewayId = deviceGatewayID
            };

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

            // device will be initialized
            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None))
            .ReturnsAsync(simulatedDevice.CreateABPTwin());

            using var request = WaitableLoRaRequest.Create(payload);
            var requestHandler = new Mock <ILoRaDataRequestHandler>(MockBehavior.Strict);

            requestHandler.Setup(x => x.ProcessRequestAsync(request, It.IsNotNull <LoRaDevice>()))
            .ReturnsAsync(new LoRaDeviceRequestProcessResult(null, request));

            var deviceFactory = new TestLoRaDeviceFactory(LoRaDeviceClient.Object, requestHandler.Object, DeviceCache, ConnectionManager);

            using var target = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, deviceFactory, DeviceCache);
            target.GetLoRaRequestQueue(request).Queue(request);

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

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

            // ensure device is in cache
            Assert.True(DeviceCache.TryGetForPayload(request.Payload, out var actualCachedLoRaDevice));

            // request was handled
            requestHandler.VerifyAll();
        }
Exemple #17
0
        private bool SaveDeviceCache(Device device)
        {
            var serializer = new JsonSerializer();

            Directory.CreateDirectory(GetProviderCacheDirectory(device.ProviderName));
            using (var streamWriter = new StreamWriter(GetDeviceCachePath(device)))
            {
                var deviceCache = new DeviceCache()
                {
                    Title             = device.Title,
                    ProviderName      = device.ProviderName,
                    DeviceHandle      = device.DeviceHandle,
                    DeviceNumber      = device.DeviceNumber,
                    DeviceBindingMenu = GetDeviceBindingMenu(device, DeviceIoType.Input, false)
                };

                serializer.Serialize(streamWriter, deviceCache);
            }

            return(true);
        }
Exemple #18
0
        public async Task When_Multiple_Devices_With_Same_DevAddr_Are_Cached_Should_Find_Matching_By_Mic(string deviceGatewayID)
        {
            var connectionManager = new Mock <ILoRaDeviceClientConnectionManager>();
            var simulatedDevice1  = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: deviceGatewayID));

            using var loraDevice1 = TestUtils.CreateFromSimulatedDevice(simulatedDevice1, connectionManager.Object);

            var simulatedDevice2 = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerConfiguration.GatewayID));

            simulatedDevice2.LoRaDevice.DeviceID = new DevEui(2).ToString();
            simulatedDevice2.LoRaDevice.NwkSKey  = TestKeys.CreateNetworkSessionKey(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF);
            using var loraDevice2 = TestUtils.CreateFromSimulatedDevice(simulatedDevice2, connectionManager.Object);

            DeviceCache.Register(loraDevice1);
            DeviceCache.Register(loraDevice2);

            var payload = simulatedDevice1.CreateUnconfirmedDataUpMessage("1234");

            var apiService = new Mock <LoRaDeviceAPIServiceBase>();

            using var request = WaitableLoRaRequest.Create(payload);
            var requestHandler = new Mock <ILoRaDataRequestHandler>(MockBehavior.Strict);

            requestHandler.Setup(x => x.ProcessRequestAsync(request, loraDevice1))
            .ReturnsAsync(new LoRaDeviceRequestProcessResult(loraDevice1, request));
            loraDevice1.SetRequestHandler(requestHandler.Object);

            using var target = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, this.loraDeviceFactoryMock.Object, DeviceCache);
            target.GetLoRaRequestQueue(request).Queue(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingSucceeded);

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

            // Device was created by factory
            this.loraDeviceFactoryMock.VerifyAll();

            requestHandler.VerifyAll();
        }
Exemple #19
0
        public void When_Cache_Clear_Is_Called_Should_Removed_Cached_Devices(string deviceGatewayID)
        {
            LoRaDeviceClient.Setup(ldc => ldc.Dispose());
            const int deviceCount = 10;
            var       deviceList  = new HashSet <LoRaDevice>();

            var apiService    = new Mock <LoRaDeviceAPIServiceBase>();
            var deviceFactory = new TestLoRaDeviceFactory(LoRaDeviceClient.Object, DeviceCache);

            using var target            = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, deviceFactory, DeviceCache);
            using var connectionManager = new SingleDeviceConnectionManager(LoRaDeviceClient.Object);

            for (var deviceID = 1; deviceID <= deviceCount; ++deviceID)
            {
                var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice((uint)deviceID, gatewayID: deviceGatewayID));
#pragma warning disable CA2000 // Dispose objects before losing scope - transfer ownership
                var device = TestUtils.CreateFromSimulatedDevice(simulatedDevice, connectionManager);
#pragma warning restore CA2000 // Dispose objects before losing scope
                DeviceCache.Register(device);
                deviceList.Add(device);
            }

            Assert.Equal(deviceCount, DeviceCache.CalculateStatistics().Count);

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

            // Device was created by factory
            this.loraDeviceFactoryMock.VerifyAll();

            // ensure all devices are in cache
            Assert.Equal(deviceCount, deviceList.Count(x => DeviceCache.TryGetByDevEui(x.DevEUI, out _)));

            target.ResetDeviceCache();
            Assert.False(deviceList.Any(x => DeviceCache.TryGetByDevEui(x.DevEUI, out _)), "Should not find devices again");
        }
Exemple #20
0
        public async Task ABP_New_Loaded_Device_With_Fcnt_1_Or_0_Should_Reset_Fcnt_And_Send_To_IotHub(
            string twinGatewayID,
            uint payloadFcntUp,
            uint?deviceTwinFcntUp,
            uint?deviceTwinFcntDown)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: null));

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

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

            // twin will be loaded
            var initialTwin = new Twin();

            initialTwin.Properties.Desired[TwinProperty.DevEUI]  = devEui.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppEui]  = simulatedDevice.LoRaDevice.AppEui?.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppKey]  = simulatedDevice.LoRaDevice.AppKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.NwkSKey] = simulatedDevice.LoRaDevice.NwkSKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppSKey] = simulatedDevice.LoRaDevice.AppSKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.DevAddr] = devAddr.ToString();
            if (twinGatewayID != null)
            {
                initialTwin.Properties.Desired[TwinProperty.GatewayID] = twinGatewayID;
            }
            initialTwin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            if (deviceTwinFcntDown.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntDown] = deviceTwinFcntDown.Value;
            }
            if (deviceTwinFcntUp.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntUp] = deviceTwinFcntUp.Value;
            }

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

            // twin will be updated with new fcnt
            int?fcntUpSavedInTwin   = null;
            int?fcntDownSavedInTwin = null;

            var shouldSaveTwin = (deviceTwinFcntDown ?? 0) != 0 || (deviceTwinFcntUp ?? 0) != 0;

            if (shouldSaveTwin)
            {
                LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
                .Callback <TwinCollection, CancellationToken>((t, _) =>
                {
                    fcntUpSavedInTwin   = (int)t[TwinProperty.FCntUp];
                    fcntDownSavedInTwin = (int)t[TwinProperty.FCntDown];
                })
                .ReturnsAsync(true);
            }

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

            // device api will be searched for payload
            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);

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

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

            // Ensure that a telemetry was sent
            Assert.NotNull(loRaDeviceTelemetry);

            // Ensure that the device twins were saved
            if (shouldSaveTwin)
            {
                Assert.NotNull(fcntDownSavedInTwin);
                Assert.NotNull(fcntUpSavedInTwin);
                Assert.Equal(0, fcntDownSavedInTwin.Value);
                Assert.Equal(0, fcntUpSavedInTwin.Value);
            }

            // 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(payloadFcntUp, loRaDevice.FCntUp);
            Assert.Equal(0U, loRaDevice.FCntDown);
            if (payloadFcntUp == 0)
            {
                Assert.False(loRaDevice.HasFrameCountChanges); // no changes
            }
            else
            {
                Assert.True(loRaDevice.HasFrameCountChanges); // should have changes!
            }
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task When_Payload_Has_Invalid_Mic_Should_Not_Send_Messages(int searchDevicesDelayMs)
        {
            // Setup
            var wrongSKey       = TestKeys.CreateNetworkSessionKey(0xEEDDFF);
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1));

            var searchResult = new SearchDevicesResult(new IoTHubDeviceInfo(simulatedDevice.DevAddr, simulatedDevice.DevEUI, "1321").AsList());

            if (searchDevicesDelayMs > 0)
            {
                LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value))
                .ReturnsAsync(searchResult, TimeSpan.FromMilliseconds(searchDevicesDelayMs));
            }
            else
            {
                LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value))
                .ReturnsAsync(searchResult);
            }

            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None))
            .ReturnsAsync(TestUtils.CreateABPTwin(simulatedDevice));

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

            // Send request #1
            var payload1 = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 2, nwkSKey: wrongSKey);

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

            await Task.Delay(2000);

            Assert.Equal(1, DeviceCache.RegistrationCount(simulatedDevice.DevAddr.Value));

            // Send request #2
            var payload2 = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 3, nwkSKey: wrongSKey);

            using var request2 = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload2);
            messageProcessor.DispatchRequest(request2);
            Assert.True(await request2.WaitCompleteAsync());
            Assert.Null(request2.ResponseDownlink);
            Assert.True(request2.ProcessingFailed);
            Assert.Equal(LoRaDeviceRequestFailedReason.NotMatchingDeviceByMicCheck, request2.ProcessingFailedReason);
            Assert.Equal(1, DeviceCache.RegistrationCount(simulatedDevice.DevAddr.Value));

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

            LoRaDeviceApi.Verify(x => x.SearchByDevAddrAsync(simulatedDevice.DevAddr.Value), Times.Exactly(1));
        }
        private async Task Join_With_Subsequent_Unconfirmed_And_Confirmed_Messages(string deviceGatewayID, uint initialFcntUp, uint initialFcntDown, uint startingPayloadFcnt, int netId, Region region)
        {
            var simulatedDevice    = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequestPayload = simulatedDevice.CreateJoinRequest();

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

            ServerConfiguration.NetId = new NetId(netId);
            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI] = devEui.ToString();
            twin.Properties.Desired[TwinProperty.AppEui] = simulatedDevice.LoRaDevice.AppEui?.ToString();
            twin.Properties.Desired[TwinProperty.AppKey] = simulatedDevice.LoRaDevice.AppKey?.ToString();
            if (deviceGatewayID != null)
            {
                twin.Properties.Desired[TwinProperty.GatewayID] = deviceGatewayID;
            }
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            twin.Properties.Reported[TwinProperty.FCntUp]       = initialFcntUp;
            twin.Properties.Reported[TwinProperty.FCntDown]     = initialFcntDown;
            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(twin);

            // Device twin will be updated
            AppSessionKey?    afterJoinAppSKey = null;
            NetworkSessionKey?afterJoinNwkSKey = null;
            string            afterJoinDevAddr = null;
            uint afterJoinFcntDown             = 0;
            uint afterJoinFcntUp = 0;

            TwinCollection actualSavedTwin = null;

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .Callback <TwinCollection, CancellationToken>((updatedTwin, _) =>
            {
                if (updatedTwin.Contains(TwinProperty.AppSKey))
                {
                    afterJoinAppSKey = AppSessionKey.Parse(updatedTwin[TwinProperty.AppSKey].Value);
                }
                if (updatedTwin.Contains(TwinProperty.NwkSKey))
                {
                    afterJoinNwkSKey = NetworkSessionKey.Parse(updatedTwin[TwinProperty.NwkSKey].Value);
                }
                if (updatedTwin.Contains(TwinProperty.DevAddr))
                {
                    afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr];
                }
                afterJoinFcntDown = updatedTwin[TwinProperty.FCntDown];
                afterJoinFcntUp   = updatedTwin[TwinProperty.FCntUp];
                actualSavedTwin   = updatedTwin;
            })
            .ReturnsAsync(true);

            // message will be sent
            var sentTelemetry = new List <LoRaDeviceTelemetry>();

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

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

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

            // multi gateway will request a next frame count down from the lora device api, prepare it
            if (string.IsNullOrEmpty(deviceGatewayID))
            {
                LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(devEui, 0, startingPayloadFcnt + 1, ServerConfiguration.GatewayID))
                .ReturnsAsync((ushort)1);
                LoRaDeviceApi
                .Setup(x => x.ExecuteFunctionBundlerAsync(devEui, It.IsAny <FunctionBundlerRequest>()))
                .ReturnsAsync(() => new FunctionBundlerResult
                {
                    AdrResult = new LoRaTools.ADR.LoRaADRResult
                    {
                        CanConfirmToDevice = false,
                        FCntDown           = 1,
                        NbRepetition       = 1,
                        TxPower            = 0
                    },
                    NextFCntDown = 1
                });
            }

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

            // Create a join request and join with the device.
            using var joinRequest =
                      CreateWaitableRequest(joinRequestPayload, constantElapsedTime: TimeSpan.FromMilliseconds(300), region: region);
            messageProcessor.DispatchRequest(joinRequest);
            Assert.True(await joinRequest.WaitCompleteAsync());
            Assert.True(joinRequest.ProcessingSucceeded, $"Failed due to '{joinRequest.ProcessingFailedReason}'.");
            Assert.NotNull(joinRequest.ResponseDownlink);
            Assert.Single(DownstreamMessageSender.DownlinkMessages);
            var downlinkJoinAcceptMessage = DownstreamMessageSender.DownlinkMessages[0];
            var joinAccept = new LoRaPayloadJoinAccept(downlinkJoinAcceptMessage.Data, simulatedDevice.LoRaDevice.AppKey.Value);

            Assert.Equal(joinAccept.DevAddr.ToString(), afterJoinDevAddr);

            // check that the device is in cache
            Assert.True(DeviceCache.TryGetByDevEui(devEui, out var loRaDevice));
            Assert.Equal(afterJoinAppSKey, loRaDevice.AppSKey);
            Assert.Equal(afterJoinNwkSKey, loRaDevice.NwkSKey);
            Assert.Equal(afterJoinDevAddr, loRaDevice.DevAddr.ToString());
            var netIdBytes = BitConverter.GetBytes(netId);

            Assert.Equal(netIdBytes[0] & 0b01111111, DevAddr.Parse(afterJoinDevAddr).NetworkId);
            if (deviceGatewayID == null)
            {
                Assert.Null(loRaDevice.GatewayID);
            }
            else
            {
                Assert.Equal(deviceGatewayID, loRaDevice.GatewayID);
            }

            // Assert that after a join the fcnt is restarted
            Assert.Equal(0U, afterJoinFcntDown);
            Assert.Equal(0U, afterJoinFcntUp);
            Assert.Equal(0U, loRaDevice.FCntUp);
            Assert.Equal(0U, loRaDevice.FCntDown);
            Assert.False(loRaDevice.HasFrameCountChanges);

            simulatedDevice.LoRaDevice.AppSKey = afterJoinAppSKey;
            simulatedDevice.LoRaDevice.NwkSKey = afterJoinNwkSKey;
            simulatedDevice.LoRaDevice.DevAddr = DevAddr.Parse(afterJoinDevAddr);

            // sends unconfirmed message with a given starting frame counter
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("100", fcnt: startingPayloadFcnt);
            var radioMetadata             = TestUtils.GenerateTestRadioMetadata();

            using var unconfirmedRequest =
                      CreateWaitableRequest(radioMetadata, unconfirmedMessagePayload,
                                            constantElapsedTime: TimeSpan.FromMilliseconds(300));
            messageProcessor.DispatchRequest(unconfirmedRequest);
            Assert.True(await unconfirmedRequest.WaitCompleteAsync());
            Assert.Null(unconfirmedRequest.ResponseDownlink);
            Assert.True(unconfirmedRequest.ProcessingSucceeded);

            // fcnt up was updated
            Assert.Equal(startingPayloadFcnt, loRaDevice.FCntUp);
            Assert.Equal(0U, loRaDevice.FCntDown);

            // If the starting payload was not 0, it is expected that it updates the framecounter char
            // The device will perform the frame counter update and at this point in time it will have the same frame counter as the desired
            // Therefore savechangesasync will set the hasframcounter change to false
            // if (startingPayloadFcnt != 0)
            // {
            //    // Frame change flag will be set, only saving every 10 messages
            //    Assert.True(loRaDevice.HasFrameCountChanges);
            // }

            Assert.Single(sentTelemetry);

            // sends confirmed message
            var confirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("200", fcnt: startingPayloadFcnt + 1);

            using var confirmedRequest = CreateWaitableRequest(confirmedMessagePayload, constantElapsedTime: TimeSpan.FromMilliseconds(300), region: region);
            messageProcessor.DispatchRequest(confirmedRequest);
            Assert.True(await confirmedRequest.WaitCompleteAsync());
            Assert.True(confirmedRequest.ProcessingSucceeded);
            Assert.NotNull(confirmedRequest.ResponseDownlink);
            Assert.Equal(2, DownstreamMessageSender.DownlinkMessages.Count);
            Assert.Equal(2, sentTelemetry.Count);
            var downstreamMessage = DownstreamMessageSender.DownlinkMessages[1];

            // validates txpk according to region
            DeviceJoinInfo deviceJoinInfo = null;

            if (region is RegionCN470RP2 cnRegion && cnRegion.TryGetJoinChannelIndex(confirmedRequest.RadioMetadata.Frequency, out var channelIndex))
            {
                deviceJoinInfo = new DeviceJoinInfo(channelIndex);
            }
            Assert.True(region.TryGetDownstreamChannelFrequency(confirmedRequest.RadioMetadata.Frequency, confirmedRequest.RadioMetadata.DataRate, deviceJoinInfo, downstreamFrequency: out var frequency));
            Assert.Equal(frequency, downstreamMessage.Rx1?.Frequency);

            var rx2Freq = region.GetDownstreamRX2Freq(null, deviceJoinInfo, NullLogger.Instance);

            Assert.Equal(rx2Freq, downstreamMessage.Rx2.Frequency);

            var rx2DataRate = region.GetDownstreamRX2DataRate(null, null, deviceJoinInfo, NullLogger.Instance);

            Assert.Equal(rx2DataRate, downstreamMessage.Rx2.DataRate);

            // fcnt up was updated
            Assert.Equal(startingPayloadFcnt + 1, loRaDevice.FCntUp);
            Assert.Equal(1U, loRaDevice.FCntDown);

            // Frame change flag will be set, only saving every 10 messages
            Assert.True(loRaDevice.HasFrameCountChanges);

            // C2D message will be checked twice (for AS923 only once, since we use the first C2D message to send the dwell time MAC command)
            var numberOfC2DMessageChecks = region is RegionAS923 ? 1 : 2;

            LoRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()), Times.Exactly(numberOfC2DMessageChecks));

            // has telemetry with both fcnt
            Assert.Single(sentTelemetry, (t) => t.Fcnt == startingPayloadFcnt);
            Assert.Single(sentTelemetry, (t) => t.Fcnt == (startingPayloadFcnt + 1));

            // should not save class C device properties
            Assert.False(actualSavedTwin.Contains(TwinProperty.Region));
            Assert.False(actualSavedTwin.Contains(TwinProperty.PreferredGatewayID));

            LoRaDeviceClient.VerifyAll();
        }
Exemple #23
0
        public async Task When_Join_Fails_Due_To_Timeout_Second_Try_Should_Reuse_Cached_Device_Twin(string deviceGatewayID)
        {
            var simulatedDevice     = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequestPayload1 = simulatedDevice.CreateJoinRequest();

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

            // Device twin will be queried twice, 1st time will take 7 seconds, 2nd time 0.1 second
            var twin = new Twin();

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

            // Device twin will be updated
            AppSessionKey?    afterJoinAppSKey = null;
            NetworkSessionKey?afterJoinNwkSKey = null;
            string            afterJoinDevAddr = null;

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .Callback <TwinCollection, CancellationToken>((updatedTwin, _) =>
            {
                afterJoinAppSKey = AppSessionKey.Parse(updatedTwin[TwinProperty.AppSKey].Value);
                afterJoinNwkSKey = NetworkSessionKey.Parse(updatedTwin[TwinProperty.NwkSKey].Value);
                afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr];
            })
            .ReturnsAsync(true);

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

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

            // 1st join request
            // Should fail
            using var joinRequest1 = CreateWaitableRequest(joinRequestPayload1, constantElapsedTime: TimeSpan.FromSeconds(7));
            messageProcessor.DispatchRequest(joinRequest1);
            Assert.True(await joinRequest1.WaitCompleteAsync());
            Assert.True(joinRequest1.ProcessingFailed);
            Assert.Null(joinRequest1.ResponseDownlink);
            Assert.Equal(LoRaDeviceRequestFailedReason.ReceiveWindowMissed, joinRequest1.ProcessingFailedReason);

            // 2nd attempt
            var joinRequestPayload2 = simulatedDevice.CreateJoinRequest();

            // setup response to this device search
            LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequestPayload2.DevNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList()));

            using var joinRequest2 = CreateWaitableRequest(joinRequestPayload2);
            messageProcessor.DispatchRequest(joinRequest2);
            Assert.True(await joinRequest2.WaitCompleteAsync());
            Assert.True(joinRequest2.ProcessingSucceeded);
            Assert.NotNull(joinRequest2.ResponseDownlink);
            Assert.Single(DownstreamMessageSender.DownlinkMessages);
            var joinRequestDownlinkMessage = DownstreamMessageSender.DownlinkMessages[0];
            var joinAccept = new LoRaPayloadJoinAccept(joinRequestDownlinkMessage.Data, simulatedDevice.LoRaDevice.AppKey.Value);

            Assert.Equal(joinAccept.DevAddr.ToString(), afterJoinDevAddr);

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

            Assert.Equal(simulatedDevice.AppKey, loRaDevice.AppKey);
            Assert.Equal(simulatedDevice.AppEui, loRaDevice.AppEui);
            Assert.Equal(afterJoinAppSKey, loRaDevice.AppSKey);
            Assert.Equal(afterJoinNwkSKey, loRaDevice.NwkSKey);
            Assert.Equal(joinAccept.DevAddr, loRaDevice.DevAddr);
            if (deviceGatewayID == null)
            {
                Assert.Null(loRaDevice.GatewayID);
            }
            else
            {
                Assert.Equal(deviceGatewayID, loRaDevice.GatewayID);
            }

            // fcnt is restarted
            Assert.Equal(0U, loRaDevice.FCntUp);
            Assert.Equal(0U, loRaDevice.FCntDown);
            Assert.False(loRaDevice.HasFrameCountChanges);

            // searching the device should happen twice
            LoRaDeviceApi.Verify(x => x.SearchAndLockForJoinAsync(ServerGatewayID, devEui, It.IsAny <DevNonce>()), Times.Exactly(2));

            // getting the device twin should happens once
            LoRaDeviceClient.Verify(x => x.GetTwinAsync(CancellationToken.None), Times.Once());

            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task ABP_Load_And_Receiving_Multiple_Unconfirmed_Should_Send_All_ToHub(ParallelTestConfiguration parallelTestConfiguration)
        {
            Console.WriteLine("---");
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(parallelTestConfiguration.DeviceID ?? 1, gatewayID: null));

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

            // Using loose mock because sometimes we might call receive async
            var looseDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Loose);

            LoRaDeviceFactory.SetClient(devEui, looseDeviceClient.Object);

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

            // message will be sent
            var sentTelemetry = new List <LoRaDeviceTelemetry>();

            looseDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Returns <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) =>
            {
                sentTelemetry.Add(t);
                var duration = parallelTestConfiguration.SendEventDuration.Next();
                Console.WriteLine($"{nameof(looseDeviceClient.Object.SendEventAsync)} sleeping for {duration}");
                return(Task.Delay(duration)
                       .ContinueWith((a) => true, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default));
            });

            // twin will be loaded
            var initialTwin = new Twin();

            initialTwin.Properties.Desired[TwinProperty.DevEUI]  = devEui.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppEui]  = simulatedDevice.LoRaDevice.AppEui?.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppKey]  = simulatedDevice.LoRaDevice.AppKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.NwkSKey] = simulatedDevice.LoRaDevice.NwkSKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.AppSKey] = simulatedDevice.LoRaDevice.AppSKey?.ToString();
            initialTwin.Properties.Desired[TwinProperty.DevAddr] = devAddr.ToString();
            if (parallelTestConfiguration.GatewayID != null)
            {
                initialTwin.Properties.Desired[TwinProperty.GatewayID] = parallelTestConfiguration.GatewayID;
            }
            initialTwin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            if (parallelTestConfiguration.DeviceTwinFcntDown.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntDown] = parallelTestConfiguration.DeviceTwinFcntDown.Value;
            }
            if (parallelTestConfiguration.DeviceTwinFcntUp.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntUp] = parallelTestConfiguration.DeviceTwinFcntUp.Value;
            }

            looseDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None))
            .Returns(() =>
            {
                var duration = parallelTestConfiguration.LoadTwinDuration.Next();
                Console.WriteLine($"{nameof(looseDeviceClient.Object.GetTwinAsync)} sleeping for {duration}");
                return(Task.Delay(duration)
                       .ContinueWith(_ => initialTwin, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default));
            });

            // twin will be updated with new fcnt
            var  expectedToSaveTwin  = parallelTestConfiguration.DeviceTwinFcntDown > 0 || parallelTestConfiguration.DeviceTwinFcntUp > 0;
            uint?fcntUpSavedInTwin   = null;
            uint?fcntDownSavedInTwin = null;

            if (expectedToSaveTwin)
            {
                looseDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
                .Returns <TwinCollection, CancellationToken>((t, _) =>
                {
                    fcntUpSavedInTwin   = (uint)t[TwinProperty.FCntUp];
                    fcntDownSavedInTwin = (uint)t[TwinProperty.FCntDown];
                    var duration        = parallelTestConfiguration.UpdateTwinDuration.Next();
                    Console.WriteLine($"{nameof(looseDeviceClient.Object.UpdateReportedPropertiesAsync)} sleeping for {duration}");
                    return(Task.Delay(duration, CancellationToken.None)
                           .ContinueWith((a) => true, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default));
                });
            }

            if (expectedToSaveTwin && string.IsNullOrEmpty(parallelTestConfiguration.GatewayID))
            {
                LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(devEui, It.IsAny <uint>(), It.IsNotNull <string>()))
                .Returns(() =>
                {
                    var duration = parallelTestConfiguration.DeviceApiResetFcntDuration.Next();
                    Console.WriteLine($"{nameof(LoRaDeviceApi.Object.ABPFcntCacheResetAsync)} sleeping for {duration}");
                    return(Task.Delay(duration)
                           .ContinueWith((a) => true, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default));
                });
            }

            // device api will be searched for payload
            LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(devAddr))
            .Returns(() =>
            {
                var duration = parallelTestConfiguration.SearchByDevAddrDuration.Next();
                Console.WriteLine($"{nameof(LoRaDeviceApi.Object.SearchByDevAddrAsync)} sleeping for {duration}");
                return(Task.Delay(duration)
                       .ContinueWith((a) => new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "abc").AsList()),
                                     CancellationToken.None,
                                     TaskContinuationOptions.ExecuteSynchronously,
                                     TaskScheduler.Default));
            });

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

            // sends unconfirmed message
            var unconfirmedMessage1 = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 1);
            var unconfirmedMessage2 = simulatedDevice.CreateUnconfirmedDataUpMessage("2", fcnt: 2);
            var unconfirmedMessage3 = simulatedDevice.CreateUnconfirmedDataUpMessage("3", fcnt: 3);

            var req1 = CreateWaitableRequest(unconfirmedMessage1, this.downstreamMessageSender);

            messageDispatcher.DispatchRequest(req1);
            await Task.Delay(parallelTestConfiguration.BetweenMessageDuration.Next());

            var req2 = CreateWaitableRequest(unconfirmedMessage2, this.downstreamMessageSender);

            messageDispatcher.DispatchRequest(req2);
            await Task.Delay(parallelTestConfiguration.BetweenMessageDuration.Next());

            var req3 = CreateWaitableRequest(unconfirmedMessage3, this.downstreamMessageSender);

            messageDispatcher.DispatchRequest(req3);
            await Task.Delay(parallelTestConfiguration.BetweenMessageDuration.Next());

            await Task.WhenAll(req1.WaitCompleteAsync(20000), req2.WaitCompleteAsync(20000), req3.WaitCompleteAsync(20000));

            var allRequests = new[] { req1, req2, req3 };

            Assert.All(allRequests, x => Assert.Null(x.ResponseDownlink));
            Assert.All(allRequests, x => Assert.True(x.ProcessingSucceeded));

            looseDeviceClient.Verify(x => x.GetTwinAsync(CancellationToken.None), Times.Exactly(1));
            if (expectedToSaveTwin)
            {
                looseDeviceClient.Verify(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>()), Times.Exactly(1));
            }

            LoRaDeviceApi.Verify(x => x.SearchByDevAddrAsync(devAddr), Times.Once);

            // Ensure that all telemetry was sent
            Assert.Equal(3, sentTelemetry.Count);

            // Ensure data was sent in order
            Assert.Equal(1, sentTelemetry[0].Fcnt);
            Assert.Equal(2, sentTelemetry[1].Fcnt);
            Assert.Equal(3, sentTelemetry[2].Fcnt);

            // Ensure that the device twins were saved
            if (expectedToSaveTwin)
            {
                Assert.NotNull(fcntDownSavedInTwin);
                Assert.NotNull(fcntUpSavedInTwin);
                Assert.Equal(0U, fcntDownSavedInTwin.Value);
                Assert.Equal(0U, fcntUpSavedInTwin.Value);
            }

            // verify that the device in device registry has correct properties and frame counters
            Assert.True(DeviceCache.TryGetForPayload(req1.Payload, out var loRaDevice));
            Assert.Equal(devAddr, loRaDevice.DevAddr);
            Assert.Equal(devEui, loRaDevice.DevEUI);
            Assert.True(loRaDevice.IsABP);
            Assert.Equal(3U, loRaDevice.FCntUp);
            Assert.Equal(0U, loRaDevice.FCntDown);
            Assert.True(loRaDevice.HasFrameCountChanges); // should have changes!

            // looseDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task When_First_Join_Fails_Due_To_Slow_Twin_Update_Retry_Second_Attempt_Should_Succeed(string deviceGatewayID)
        {
            var simulatedDevice     = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequestPayload1 = simulatedDevice.CreateJoinRequest();
            var joinRequestPayload2 = 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
            AppSessionKey?    afterJoin2AppSKey = null;
            NetworkSessionKey?afterJoin2NwkSKey = null;
            DevAddr?          afterJoin2DevAddr = null;

            var mockSequence = new MockSequence();

            LoRaDeviceClient.InSequence(mockSequence)
            .Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .Returns <TwinCollection, CancellationToken>(async(_, token) =>
            {
                try
                {
                    await Task.Delay(TimeSpan.FromSeconds(20), token);
                    Assert.True(false, "Token timeout expected");
                }
                catch (OperationCanceledException) { }
                return(false);
            });
            LoRaDeviceClient.InSequence(mockSequence)
            .Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .Returns <TwinCollection, CancellationToken>((updatedTwin, token) =>
            {
                afterJoin2AppSKey = updatedTwin.SafeRead <AppSessionKey>(TwinProperty.AppSKey);
                afterJoin2NwkSKey = updatedTwin.SafeRead <NetworkSessionKey>(TwinProperty.NwkSKey);
                afterJoin2DevAddr = updatedTwin.SafeRead <DevAddr>(TwinProperty.DevAddr);
                return(Task.FromResult(true));
            });

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

            LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequestPayload2.DevNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList()));

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

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

            using var joinRequest1 = CreateWaitableRequest(joinRequestPayload1, useRealTimer: true);
            messageProcessor.DispatchRequest(joinRequest1);
            await Task.Delay(TimeSpan.FromSeconds(7));

            using var joinRequest2 = CreateWaitableRequest(joinRequestPayload2, useRealTimer: true);
            messageProcessor.DispatchRequest(joinRequest2);

            await Task.WhenAll(joinRequest1.WaitCompleteAsync(), joinRequest2.WaitCompleteAsync());

            Assert.True(joinRequest1.ProcessingFailed);
            Assert.Null(joinRequest1.ResponseDownlink);
            Assert.Equal(LoRaDeviceRequestFailedReason.IoTHubProblem, joinRequest1.ProcessingFailedReason);
            Assert.True(joinRequest2.ProcessingSucceeded);
            Assert.NotNull(joinRequest2.ResponseDownlink);
            Assert.Single(DownstreamMessageSender.DownlinkMessages);

            Assert.True(DeviceCache.TryGetByDevEui(devEui, out var loRaDevice));
            Assert.True(loRaDevice.IsOurDevice);
            Assert.Equal(afterJoin2DevAddr, loRaDevice.DevAddr);
            Assert.Equal(afterJoin2NwkSKey, loRaDevice.NwkSKey);
            Assert.Equal(afterJoin2AppSKey, loRaDevice.AppSKey);

            // get twin should happen only once
            LoRaDeviceClient.Verify(x => x.GetTwinAsync(CancellationToken.None), Times.Once());
            LoRaDeviceClient.VerifyAll();
            LoRaDeviceApi.VerifyAll();
        }
        public async Task When_ABP_Sends_Upstream_Followed_By_DirectMethod_Should_Send_Upstream_And_Downstream(string deviceGatewayID, uint fcntDownFromTwin, uint fcntDelta, Region region)
        {
            const uint payloadFcnt = 2; // to avoid relax mode reset

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            var expectedFcntDown = fcntDownFromTwin + Constants.MaxFcntUnsavedDelta + fcntDelta;

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

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

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

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

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

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

            LoRaDeviceApi.VerifyAll();
            LoRaDeviceClient.VerifyAll();
        }
        public async Task 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 When_Getting_Device_Information_From_Twin_Returns_JoinAccept(string deviceGatewayID)
        {
            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();
            if (deviceGatewayID != null)
            {
                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
            AppSessionKey?    afterJoinAppSKey = null;
            NetworkSessionKey?afterJoinNwkSKey = null;
            string            afterJoinDevAddr = null;

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .Callback <TwinCollection, CancellationToken>((updatedTwin, _) =>
            {
                afterJoinAppSKey = AppSessionKey.Parse(updatedTwin[TwinProperty.AppSKey].Value);
                afterJoinNwkSKey = NetworkSessionKey.Parse(updatedTwin[TwinProperty.NwkSKey].Value);
                afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr];
            })
            .ReturnsAsync(true);

            // Lora device api will be search by devices with matching deveui,
            LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, 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(joinAccept.DevAddr.ToString(), afterJoinDevAddr);

            // check that the device is in cache
            Assert.True(DeviceCache.HasRegistrations(joinAccept.DevAddr));
            Assert.True(DeviceCache.TryGetByDevEui(devEui, out var cachedDevice));
            Assert.Equal(afterJoinAppSKey, cachedDevice.AppSKey);
            Assert.Equal(afterJoinNwkSKey, cachedDevice.NwkSKey);
            Assert.Equal(joinAccept.DevAddr, cachedDevice.DevAddr);
            Assert.True(cachedDevice.IsOurDevice);
            if (deviceGatewayID == null)
            {
                Assert.Null(cachedDevice.GatewayID);
            }
            else
            {
                Assert.Equal(deviceGatewayID, cachedDevice.GatewayID);
            }

            // fcnt is restarted
            Assert.Equal(0U, cachedDevice.FCntUp);
            Assert.Equal(0U, cachedDevice.FCntDown);
            Assert.False(cachedDevice.HasFrameCountChanges);
        }
Exemple #29
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_Device_Is_Found_In_Api_Should_Update_Twin_And_Return()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: ServerConfiguration.GatewayID));

            simulatedDevice.LoRaDevice.NwkSKey = null;
            simulatedDevice.LoRaDevice.AppSKey = null;

            var joinRequest = simulatedDevice.CreateJoinRequest();

            // this former join request is just to set the loradevice cache to another devnonce.
            _ = simulatedDevice.CreateJoinRequest();

            var devEui = simulatedDevice.LoRaDevice.DevEui;

            LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(It.IsNotNull <string>(), devEui, joinRequest.DevNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(null, devEui, "123")
            {
                GatewayId = ServerConfiguration.GatewayID
            }.AsList()));

            // Ensure that the device twin was updated
            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.Is <TwinCollection>((t) =>
                                                                                               t.Contains(TwinProperty.DevAddr) && t.Contains(TwinProperty.FCntDown)), It.IsAny <CancellationToken>()))
            .ReturnsAsync(true);

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

            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(), 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.AppKey.Value);

            Assert.Equal(1, DeviceCache.RegistrationCount(joinAccept.DevAddr));
            Assert.True(DeviceCache.TryGetByDevEui(devEui, out var loRaDevice));
            Assert.Equal(joinAccept.DevAddr, loRaDevice.DevAddr);

            // Device properties were set with the computes values of the join operation
            Assert.Equal(joinAccept.AppNonce, loRaDevice.AppNonce);
            Assert.NotNull(loRaDevice.NwkSKey);
            Assert.NotNull(loRaDevice.AppSKey);
            Assert.True(loRaDevice.IsOurDevice);

            // Device frame counts were reset
            Assert.Equal(0U, loRaDevice.FCntDown);
            Assert.Equal(0U, loRaDevice.FCntUp);
            Assert.False(loRaDevice.HasFrameCountChanges);

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