示例#1
0
        public void When_ResetFcnt_In_Device_With_Pending_Changes_Should_Have_HasFrameCountChanges_True()
        {
            var devAddr = new DevAddr(0x1231);

            // Non zero fcnt up
            using var target = CreateDefaultDevice();
            target.SetFcntUp(1);
            target.AcceptFrameCountChanges();
            target.ResetFcnt();
            Assert.Equal(0U, target.FCntUp);
            Assert.Equal(0U, target.FCntDown);
            Assert.True(target.HasFrameCountChanges);

            // Non zero fcnt down
            using var secondConnectionManager = new SingleDeviceConnectionManager(this.loRaDeviceClient.Object);
            using var secondTarget            = new LoRaDevice(devAddr, new DevEui(0x12312), secondConnectionManager);
            secondTarget.SetFcntDown(1);
            secondTarget.AcceptFrameCountChanges();
            secondTarget.ResetFcnt();
            Assert.Equal(0U, secondTarget.FCntUp);
            Assert.Equal(0U, secondTarget.FCntDown);
            Assert.True(secondTarget.HasFrameCountChanges);

            // Non zero fcnt down and up
            using var thirdConnectionManager = new SingleDeviceConnectionManager(this.loRaDeviceClient.Object);
            using var thirdTarget            = new LoRaDevice(devAddr, new DevEui(0x12312), thirdConnectionManager);
            thirdTarget.SetFcntDown(1);
            thirdTarget.SetFcntDown(2);
            thirdTarget.AcceptFrameCountChanges();
            thirdTarget.ResetFcnt();
            Assert.Equal(0U, thirdTarget.FCntUp);
            Assert.Equal(0U, thirdTarget.FCntDown);
            Assert.True(thirdTarget.HasFrameCountChanges);
        }
        public async Task When_Device_Api_Throws_Error_Should_Not_Create_Any_Device()
        {
            var devAddr = new DevAddr(0x039090);

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

            apiService.Setup(x => x.SearchByDevAddrAsync(It.IsAny <DevAddr>()))
            .Throws(new InvalidOperationException());

            var deviceFactory = new Mock <ILoRaDeviceFactory>(MockBehavior.Strict);

            using var deviceCache = LoRaDeviceCacheDefault.CreateDefault();
            var target = new DeviceLoaderSynchronizer(
                devAddr,
                apiService.Object,
                deviceFactory.Object,
                new NetworkServerConfiguration(),
                deviceCache,
                null,
                NullLogger <DeviceLoaderSynchronizer> .Instance);

            var ex = await Assert.ThrowsAsync <LoRaProcessingException>(async() => await target.LoadAsync());

            Assert.IsType <InvalidOperationException>(ex.InnerException);

            Assert.Equal(0, deviceCache.CalculateStatistics().Count);

            // Device was searched by DevAddr
            apiService.VerifyAll();
        }
示例#3
0
        public override byte[] GetByteMessage()
        {
            List <byte> messageArray = new List <byte>();

            messageArray.AddRange(Mhdr.ToArray());
            DevAddr.Span.Reverse();
            messageArray.AddRange(DevAddr.ToArray());
            DevAddr.Span.Reverse();
            messageArray.AddRange(Fctrl.ToArray());
            messageArray.AddRange(Fcnt.ToArray());
            if (!Fopts.Span.IsEmpty)
            {
                messageArray.AddRange(Fopts.ToArray());
            }
            if (!Fport.Span.IsEmpty)
            {
                messageArray.AddRange(Fport.ToArray());
            }
            if (!Frmpayload.Span.IsEmpty)
            {
                messageArray.AddRange(Frmpayload.ToArray());
            }
            if (Mic.Span != null)
            {
                messageArray.AddRange(Mic.Span.ToArray());
            }
            return(messageArray.ToArray());
        }
示例#4
0
        public override byte[] PerformEncryption(string appSkey)
        {
            byte[] pt;
            if (!CfList.Span.IsEmpty)
            {
                pt = AppNonce.ToArray().Concat(NetID.ToArray()).Concat(DevAddr.ToArray()).Concat(DlSettings.ToArray()).Concat(RxDelay.ToArray()).Concat(CfList.ToArray()).Concat(Mic.ToArray()).ToArray();
            }
            else
            {
                pt = AppNonce.ToArray().Concat(NetID.ToArray()).Concat(DevAddr.ToArray()).Concat(DlSettings.ToArray()).Concat(RxDelay.ToArray()).Concat(Mic.ToArray()).ToArray();
            }

            Aes aes = new AesManaged();

            aes.Key     = ConversionHelper.StringToByteArray(appSkey);
            aes.IV      = new byte[16];
            aes.Mode    = CipherMode.ECB;
            aes.Padding = PaddingMode.None;

            ICryptoTransform cipher;

            cipher = aes.CreateDecryptor();
            var encryptedPayload = cipher.TransformFinalBlock(pt, 0, pt.Length);

            RawMessage = new byte[encryptedPayload.Length];
            Array.Copy(encryptedPayload, 0, RawMessage, 0, encryptedPayload.Length);
            return(encryptedPayload);
        }
示例#5
0
        // Creates a new instance of NetworkServerConfiguration by reading values from environment variables
        public static NetworkServerConfiguration CreateFromEnvironmentVariables()
        {
            var config = new NetworkServerConfiguration();

            // Create case insensitive dictionary from environment variables
            var envVars = new CaseInsensitiveEnvironmentVariables(Environment.GetEnvironmentVariables());

            config.RunningAsIoTEdgeModule = !string.IsNullOrEmpty(envVars.GetEnvVar("IOTEDGE_APIVERSION", string.Empty));
            config.IoTHubHostName         = envVars.GetEnvVar("IOTEDGE_IOTHUBHOSTNAME", string.Empty);
            config.GatewayHostName        = envVars.GetEnvVar("IOTEDGE_GATEWAYHOSTNAME", string.Empty);
            config.EnableGateway          = envVars.GetEnvVar("ENABLE_GATEWAY", config.EnableGateway);
            config.GatewayID    = envVars.GetEnvVar("IOTEDGE_DEVICEID", string.Empty);
            config.HttpsProxy   = envVars.GetEnvVar("HTTPS_PROXY", string.Empty);
            config.Rx2DataRate  = envVars.GetEnvVar("RX2_DATR", -1) is var datrNum && (DataRateIndex)datrNum is var datr && Enum.IsDefined(datr) ? datr : null;
            config.Rx2Frequency = envVars.GetEnvVar("RX2_FREQ") is { } someFreq?Hertz.Mega(someFreq) : null;

            config.IoTEdgeTimeout = envVars.GetEnvVar("IOTEDGE_TIMEOUT", config.IoTEdgeTimeout);

            // facadeurl is allowed to be null as the value is coming from the twin in production.
            var facadeUrl = envVars.GetEnvVar("FACADE_SERVER_URL", string.Empty);

            config.FacadeServerUrl     = string.IsNullOrEmpty(facadeUrl) ? null : new Uri(envVars.GetEnvVar("FACADE_SERVER_URL", string.Empty));
            config.FacadeAuthCode      = envVars.GetEnvVar("FACADE_AUTH_CODE", string.Empty);
            config.LogLevel            = envVars.GetEnvVar("LOG_LEVEL", config.LogLevel);
            config.LogToConsole        = envVars.GetEnvVar("LOG_TO_CONSOLE", config.LogToConsole);
            config.LogToTcp            = envVars.GetEnvVar("LOG_TO_TCP", config.LogToTcp);
            config.LogToHub            = envVars.GetEnvVar("LOG_TO_HUB", config.LogToHub);
            config.LogToTcpAddress     = envVars.GetEnvVar("LOG_TO_TCP_ADDRESS", string.Empty);
            config.LogToTcpPort        = envVars.GetEnvVar("LOG_TO_TCP_PORT", config.LogToTcpPort);
            config.NetId               = new NetId(envVars.GetEnvVar("NETID", config.NetId.NetworkId));
            config.AllowedDevAddresses = envVars.GetEnvVar("AllowedDevAddresses", string.Empty)
                                         .Split(";")
                                         .Select(s => DevAddr.TryParse(s, out var devAddr) ? (true, Value: devAddr) : default)
示例#6
0
        /// <summary>把消息写入到数据流中</summary>
        /// <param name="stream">数据流</param>
        /// <param name="context">上下文</param>
        public virtual Boolean Write(Stream stream, Object context)
        {
            stream.WriteByte(MHDR);
            stream.Write(DevAddr.GetBytes());
            stream.Write((Byte)FCtrl);

            Payload?.CopyTo(stream);

            return(true);
        }
示例#7
0
            public static async Task <DevInfo> CheckDevice2Async(ServiceAsync svc, int devId)
            {
                var lastKnownState = svc.GetKnownDevStateAsync(devId);

                DevAddr addr = await svc.GetDevAddrAsync(devId);

                DevReport devReport = await svc.GetCurrDevReportAsync(addr);

                return(new DevInfo(await lastKnownState, devReport));
            }
示例#8
0
            public static Task <DevInfo> CheckDeviceAsync(Service svc, int devId)
            {
                DevState lastKnownState = null;

                Task.Run(() => { lastKnownState = svc.GetKnownDevState(devId); });
                return(Task.Run(() => {
                    DevAddr addr = svc.GetDevAddr(devId);
                    DevReport devReport = svc.GetCurrDevReport(addr);
                    return new DevInfo(lastKnownState, devReport);
                }));
            }
示例#9
0
        public async Task When_StationEui_Missing_Should_Fail()
        {
            var devAddr   = new DevAddr(0x023637F8);
            var appSKey   = TestKeys.CreateAppSessionKey(0xABC0200000000000, 0x09);
            var nwkSKey   = TestKeys.CreateNetworkSessionKey(0xABC0200000000000, 0x09);
            var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: ServerGatewayID));
            var devEUI    = simDevice.DevEUI;

            simDevice.SetupJoin(appSKey, nwkSKey, devAddr);

            this.deviceApi.Setup(x => x.GetPrimaryKeyByEuiAsync(devEUI))
            .ReturnsAsync("123");

            var twin = simDevice.CreateOTAATwin(
                desiredProperties: new Dictionary <string, object>
            {
                { TwinProperty.RX2DataRate, "10" }
            },
                reportedProperties: new Dictionary <string, object>
            {
                { TwinProperty.RX2DataRate, 10 },
                { TwinProperty.Region, LoRaRegionType.US915.ToString() },
                // OTAA device, already joined
                { TwinProperty.DevAddr, devAddr.ToString() },
                { TwinProperty.AppSKey, appSKey.ToString() },
                { TwinProperty.NwkSKey, nwkSKey.ToString() },
            });

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

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

            var target = new DefaultClassCDevicesMessageSender(
                this.serverConfiguration,
                this.loRaDeviceRegistry,
                this.downstreamMessageSender.Object,
                this.frameCounterStrategyProvider,
                NullLogger <DefaultClassCDevicesMessageSender> .Instance,
                TestMeter.Instance);

            Assert.False(await target.SendAsync(c2dToDeviceMessage));

            this.downstreamMessageSender.VerifyAll();
            this.deviceApi.VerifyAll();
            this.deviceClient.VerifyAll();
        }
示例#10
0
        public bool TryGetInfo(DevAddr devAddr, out IList <DevAddrCacheInfo> info)
        {
            var tmp = this.cacheStore.GetHashObject(GenerateKey(devAddr));

            if (tmp?.Length > 0)
            {
                info = new List <DevAddrCacheInfo>(tmp.Length);
                foreach (var tm in tmp)
                {
                    info.Add(JsonConvert.DeserializeObject <DevAddrCacheInfo>(tm.Value));
                }
            }
            else
            {
                info = null;
            }

            return(info?.Count > 0);
        }
        private DeviceLoaderSynchronizer GetOrCreateLoadingDevicesRequestQueue(DevAddr devAddr)
        {
            // Need to get and ensure it has started since the GetOrAdd can create multiple objects
            // https://github.com/aspnet/Extensions/issues/708
            lock (this.getOrCreateLoadingDevicesRequestQueueLock)
            {
                return(this.cache.GetOrCreate(
                           GetDevLoaderCacheKey(devAddr),
                           (ce) =>
                {
                    var cts = new CancellationTokenSource();
                    ce.ExpirationTokens.Add(new CancellationChangeToken(cts.Token));

                    var loader = new DeviceLoaderSynchronizer(
                        devAddr,
                        this.loRaDeviceAPIService,
                        this.deviceFactory,
                        this.configuration,
                        this.deviceCache,
                        this.initializers,
                        this.loggerFactory.CreateLogger <DeviceLoaderSynchronizer>());

                    _ = loader.LoadAsync().ContinueWith((t) =>
                    {
                        // If the operation to load was successfull
                        // wait for 30 seconds for pending requests to go through and avoid additional calls
                        if (t.IsCompletedSuccessfully && !loader.HasLoadingDeviceError && DevAddrReloadInterval > TimeSpan.Zero)
                        {
                            // remove from cache after 30 seconds
                            cts.CancelAfter(DevAddrReloadInterval);
                        }
                        else
                        {
                            // remove from cache now
                            cts.Cancel();
                        }
                    }, TaskScheduler.Default);

                    return loader;
                }));
            }
        }
 internal DeviceLoaderSynchronizer(
     DevAddr devAddr,
     LoRaDeviceAPIServiceBase loRaDeviceAPIService,
     ILoRaDeviceFactory deviceFactory,
     NetworkServerConfiguration configuration,
     LoRaDeviceCache deviceCache,
     HashSet <ILoRaDeviceInitializer> initializers,
     ILogger <DeviceLoaderSynchronizer> logger)
 {
     this.loRaDeviceAPIService = loRaDeviceAPIService;
     this.deviceFactory        = deviceFactory;
     this.configuration        = configuration;
     this.devAddr         = devAddr;
     this.loraDeviceCache = deviceCache;
     this.initializers    = initializers;
     this.logger          = logger;
     this.state           = LoaderState.QueryingDevices;
     this.queueLock       = new object();
     this.queuedRequests  = new List <LoRaRequest>();
 }
示例#13
0
 public UpstreamDataFrame(MacHeader macHeader,
                          DevAddr devAddress,
                          FrameControlFlags fctrlFlags,
                          ushort counter,
                          string options,
                          FramePort?port,
                          string payload,
                          Mic mic,
                          RadioMetadata radioMetadata)
 {
     MacHeader         = macHeader;
     DevAddr           = devAddress;
     FrameControlFlags = fctrlFlags;
     Counter           = counter;
     Options           = options;
     Port          = port;
     Payload       = payload;
     Mic           = mic;
     RadioMetadata = radioMetadata;
 }
示例#14
0
        public void When_ResetFcnt_In_NonZero_FcntUp_Or_FcntDown_Should_Have_HasFrameCountChanges_True()
        {
            var devAddr = new DevAddr(0x1231);

            // Non zero fcnt up
            using var target = CreateDefaultDevice();
            target.SetFcntUp(1);
            target.AcceptFrameCountChanges();
            target.ResetFcnt();
            Assert.Equal(0U, target.FCntUp);
            Assert.Equal(0U, target.FCntDown);
            Assert.True(target.HasFrameCountChanges);

            // Non zero fcnt down
            using var secondConnectionManager = new SingleDeviceConnectionManager(this.loRaDeviceClient.Object);
            using var secondTarget            = new LoRaDevice(devAddr, new DevEui(0x12312), secondConnectionManager);
            secondTarget.SetFcntDown(1);
            secondTarget.AcceptFrameCountChanges();
            secondTarget.ResetFcnt();
            Assert.Equal(0U, secondTarget.FCntUp);
            Assert.Equal(0U, secondTarget.FCntDown);
            Assert.Equal(0U, secondTarget.LastSavedFCntUp);
            Assert.Equal(1U, secondTarget.LastSavedFCntDown);
            Assert.True(secondTarget.HasFrameCountChanges);

            // Non zero fcnt down and up
            using var thirdConnectionManager = new SingleDeviceConnectionManager(this.loRaDeviceClient.Object);
            using var thirdTarget            = new LoRaDevice(devAddr, new DevEui(0x12312), thirdConnectionManager);
            thirdTarget.SetFcntDown(1);
            thirdTarget.SetFcntDown(2);
            thirdTarget.AcceptFrameCountChanges();
            thirdTarget.ResetFcnt();
            Assert.Equal(0U, thirdTarget.FCntUp);
            Assert.Equal(0U, thirdTarget.FCntDown);
            Assert.Equal(0U, thirdTarget.LastSavedFCntUp);
            Assert.Equal(2U, thirdTarget.LastSavedFCntDown);
            Assert.Empty(thirdTarget.PreferredGatewayID);
            Assert.Equal(LoRaRegionType.NotSet, thirdTarget.LoRaRegion);

            Assert.True(thirdTarget.HasFrameCountChanges);
        }
示例#15
0
        public async Task When_Querying_Devices_And_Finds_No_Gateway_For_Class_C_Should_Return_InternalServerError()
        {
            var devEui     = new DevEui(0123456789);
            var devAddr    = new DevAddr(03010101);
            var deviceTwin = new Twin
            {
                Properties = new TwinProperties()
                {
                    Desired  = new TwinCollection($"{{\"DevAddr\": \"{devAddr}\", \"ClassType\": \"C\"}}"),
                    Reported = new TwinCollection(),
                }
            };

            var query = new Mock <IQuery>(MockBehavior.Strict);

            query.Setup(x => x.HasMoreResults).Returns(true);
            query.Setup(x => x.GetNextAsTwinAsync())
            .ReturnsAsync(new[] { deviceTwin });

            this.registryManager.Setup(x => x.CreateQuery(It.IsNotNull <string>(), It.IsAny <int?>()))
            .Returns(query.Object);

            var actualMessage = new LoRaCloudToDeviceMessage()
            {
                Fport   = TestPort,
                Payload = "hello",
            };

            var actual = await this.sendCloudToDeviceMessage.SendCloudToDeviceMessageImplementationAsync(
                devEui,
                actualMessage);

            var result = Assert.IsType <ObjectResult>(actual);

            Assert.Equal(500, result.StatusCode);
            Assert.Equal("Class C devices must sent at least one message upstream. None has been received", result.Value.ToString());

            this.serviceClient.VerifyAll();
            this.registryManager.VerifyAll();
            query.VerifyAll();
        }
示例#16
0
        public void When_Device_Has_No_Connection_Timeout_Should_Disconnect()
        {
            var deviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict);

            deviceClient.Setup(dc => dc.Dispose());
            using var cache   = new MemoryCache(new MemoryCacheOptions());
            using var manager = new LoRaDeviceClientConnectionManager(cache, NullLogger <LoRaDeviceClientConnectionManager> .Instance);
            using var device  = new LoRaDevice(DevAddr.Private0(0), new DevEui(0x0123456789), manager);
            manager.Register(device, deviceClient.Object);

            var activity = device.BeginDeviceClientConnectionActivity();

            Assert.NotNull(activity);

            deviceClient.Setup(x => x.Disconnect())
            .Returns(true);

            Assert.True(device.TryDisconnect());

            deviceClient.Verify(x => x.Disconnect(), Times.Once());
        }
示例#17
0
文件: Dal.cs 项目: broersa/mylorawan
        public async Task <DevAddr> GetFreeDevAddr(long nwkid)
        {
            DevAddr returnvalue = null;

            await this.Connect();

            using (var cmd = new NpgsqlCommand("select deakey, deanwkid, deanwkaddr from devaddrs left outer join sessions on sesdea=deakey where sesactive is null or sesactive <= now() limit 1", _SqlConnection, _SqlTransaction))
            {
                using (var dr = await cmd.ExecuteReaderAsync())
                {
                    if (!(await dr.ReadAsync()))
                    {
                        throw new NotFoundException();
                    }
                    returnvalue = new DevAddr()
                    {
                        Id      = dr.GetInt64(0),
                        NwkId   = dr.GetInt64(1),
                        NwkAddr = dr.GetInt64(2)
                    };
                }
            }
            return(returnvalue);
        }
示例#18
0
        public void When_Needed_Should_Reconnect_Client()
        {
            var deviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict);

            deviceClient.Setup(dc => dc.Dispose());
            using var cache         = new MemoryCache(new MemoryCacheOptions());
            using var manager       = new LoRaDeviceClientConnectionManager(cache, NullLogger <LoRaDeviceClientConnectionManager> .Instance);
            using var device        = new LoRaDevice(DevAddr.Private0(0), new DevEui(0x0123456789), manager);
            device.KeepAliveTimeout = 60;
            manager.Register(device, deviceClient.Object);

            deviceClient.Setup(x => x.EnsureConnected())
            .Returns(true);

            deviceClient.Setup(x => x.Disconnect())
            .Returns(true);

            using (var activity1 = device.BeginDeviceClientConnectionActivity())
            {
                Assert.NotNull(activity1);
            }

            Assert.True(device.TryDisconnect());

            using (var activity2 = device.BeginDeviceClientConnectionActivity())
            {
                Assert.NotNull(activity2);

                Assert.False(device.TryDisconnect());
            }

            Assert.True(device.TryDisconnect());

            deviceClient.Verify(x => x.EnsureConnected(), Times.Exactly(2));
            deviceClient.Verify(x => x.Disconnect(), Times.Exactly(2));
        }
示例#19
0
 private static string GenerateKey(DevAddr devAddr) => CacheKeyPrefix + devAddr;
示例#20
0
        private static Twin SetupTwins(uint?deviceFcntUp, uint?deviceFcntDown, uint?startFcntUp, uint?startFcntDown, bool abpRelaxed, bool supports32Bit, SimulatedDevice simulatedDevice, DevEui devEui, DevAddr devAddr)
        {
            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();
            initialTwin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            initialTwin.Properties.Desired[TwinProperty.GatewayID]     = simulatedDevice.LoRaDevice.GatewayID;
            initialTwin.Properties.Desired[TwinProperty.ABPRelaxMode]  = abpRelaxed;

            initialTwin.Properties.Desired[TwinProperty.Supports32BitFCnt] = supports32Bit;

            if (deviceFcntUp.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntUp] = deviceFcntUp.Value;
            }
            if (deviceFcntDown.HasValue)
            {
                initialTwin.Properties.Reported[TwinProperty.FCntDown] = deviceFcntDown.Value;
            }
            if (startFcntUp.HasValue)
            {
                initialTwin.Properties.Desired[TwinProperty.FCntUpStart] = startFcntUp.Value;
            }
            if (startFcntDown.HasValue)
            {
                initialTwin.Properties.Desired[TwinProperty.FCntDownStart] = startFcntDown.Value;
            }
            return(initialTwin);
        }
        internal async Task ProcessJoinRequestAsync(LoRaRequest request)
        {
            LoRaDevice loRaDevice = null;
            var        loraRegion = request.Region;

            try
            {
                var timeWatcher       = request.GetTimeWatcher();
                var processingTimeout = timeWatcher.GetRemainingTimeToJoinAcceptSecondWindow() - TimeSpan.FromMilliseconds(100);
                using var joinAcceptCancellationToken = new CancellationTokenSource(processingTimeout > TimeSpan.Zero ? processingTimeout : TimeSpan.Zero);

                var joinReq = (LoRaPayloadJoinRequest)request.Payload;

                var devEui = joinReq.DevEUI;

                using var scope = this.logger.BeginDeviceScope(devEui);

                this.logger.LogInformation("join request received");

                if (this.concentratorDeduplication.CheckDuplicateJoin(request) is ConcentratorDeduplicationResult.Duplicate)
                {
                    request.NotifyFailed(loRaDevice, LoRaDeviceRequestFailedReason.DeduplicationDrop);
                    // we do not log here as the concentratorDeduplication service already does more detailed logging
                    return;
                }

                loRaDevice = await this.deviceRegistry.GetDeviceForJoinRequestAsync(devEui, joinReq.DevNonce);

                if (loRaDevice == null)
                {
                    request.NotifyFailed(devEui.ToString(), LoRaDeviceRequestFailedReason.UnknownDevice);
                    // we do not log here as we assume that the deviceRegistry does a more informed logging if returning null
                    return;
                }

                if (loRaDevice.AppKey is null)
                {
                    this.logger.LogError("join refused: missing AppKey for OTAA device");
                    request.NotifyFailed(loRaDevice, LoRaDeviceRequestFailedReason.InvalidJoinRequest);
                    return;
                }

                var appKey = loRaDevice.AppKey.Value;

                this.joinRequestCounter?.Add(1);

                if (loRaDevice.AppEui != joinReq.AppEui)
                {
                    this.logger.LogError("join refused: AppEUI for OTAA does not match device");
                    request.NotifyFailed(loRaDevice, LoRaDeviceRequestFailedReason.InvalidJoinRequest);
                    return;
                }

                if (!joinReq.CheckMic(appKey))
                {
                    this.logger.LogError("join refused: invalid MIC");
                    request.NotifyFailed(loRaDevice, LoRaDeviceRequestFailedReason.JoinMicCheckFailed);
                    return;
                }

                // Make sure that is a new request and not a replay
                if (loRaDevice.DevNonce is { } devNonce&& devNonce == joinReq.DevNonce)
                {
                    if (string.IsNullOrEmpty(loRaDevice.GatewayID))
                    {
                        this.logger.LogInformation("join refused: join already processed by another gateway");
                    }
                    else
                    {
                        this.logger.LogError("join refused: DevNonce already used by this device");
                    }

                    request.NotifyFailed(loRaDevice, LoRaDeviceRequestFailedReason.JoinDevNonceAlreadyUsed);
                    return;
                }

                // Check that the device is joining through the linked gateway and not another
                if (!loRaDevice.IsOurDevice)
                {
                    this.logger.LogInformation("join refused: trying to join not through its linked gateway, ignoring join request");
                    request.NotifyFailed(loRaDevice, LoRaDeviceRequestFailedReason.HandledByAnotherGateway);
                    return;
                }

                var netId    = this.configuration.NetId;
                var appNonce = new AppNonce(RandomNumberGenerator.GetInt32(toExclusive: AppNonce.MaxValue + 1));
                var appSKey  = OTAAKeysGenerator.CalculateAppSessionKey(appNonce, netId, joinReq.DevNonce, appKey);
                var nwkSKey  = OTAAKeysGenerator.CalculateNetworkSessionKey(appNonce, netId, joinReq.DevNonce, appKey);
                var address  = RandomNumberGenerator.GetInt32(toExclusive: DevAddr.MaxNetworkAddress + 1);
                // The 7 LBS of the NetID become the NwkID of a DevAddr:
                var devAddr = new DevAddr(unchecked ((byte)netId.NetworkId), address);

                var oldDevAddr = loRaDevice.DevAddr;

                if (!timeWatcher.InTimeForJoinAccept())
                {
                    this.receiveWindowMisses?.Add(1);
                    // in this case it's too late, we need to break and avoid saving twins
                    this.logger.LogInformation("join refused: processing of the join request took too long, sending no message");
                    request.NotifyFailed(loRaDevice, LoRaDeviceRequestFailedReason.ReceiveWindowMissed);
                    return;
                }

                var updatedProperties = new LoRaDeviceJoinUpdateProperties
                {
                    DevAddr            = devAddr,
                    NwkSKey            = nwkSKey,
                    AppSKey            = appSKey,
                    AppNonce           = appNonce,
                    DevNonce           = joinReq.DevNonce,
                    NetId              = netId,
                    Region             = request.Region.LoRaRegion,
                    PreferredGatewayID = this.configuration.GatewayID,
                };

                if (loRaDevice.ClassType == LoRaDeviceClassType.C)
                {
                    updatedProperties.SavePreferredGateway = true;
                    updatedProperties.SaveRegion           = true;
                    updatedProperties.StationEui           = request.StationEui;
                }

                DeviceJoinInfo deviceJoinInfo = null;
                if (request.Region.LoRaRegion == LoRaRegionType.CN470RP2)
                {
                    if (request.Region.TryGetJoinChannelIndex(request.RadioMetadata.Frequency, out var channelIndex))
                    {
                        updatedProperties.CN470JoinChannel = channelIndex;
                        deviceJoinInfo = new DeviceJoinInfo(channelIndex);
                    }
                    else
                    {
                        this.logger.LogError("failed to retrieve the join channel index for device");
                    }
                }

                var deviceUpdateSucceeded = await loRaDevice.UpdateAfterJoinAsync(updatedProperties, joinAcceptCancellationToken.Token);

                if (!deviceUpdateSucceeded)
                {
                    this.logger.LogError("join refused: join request could not save twin");
                    request.NotifyFailed(loRaDevice, LoRaDeviceRequestFailedReason.IoTHubProblem);
                    return;
                }

                var windowToUse = timeWatcher.ResolveJoinAcceptWindowToUse();
                if (windowToUse is null)
                {
                    this.receiveWindowMisses?.Add(1);
                    this.logger.LogInformation("join refused: processing of the join request took too long, sending no message");
                    request.NotifyFailed(loRaDevice, LoRaDeviceRequestFailedReason.ReceiveWindowMissed);
                    return;
                }

                this.deviceRegistry.UpdateDeviceAfterJoin(loRaDevice, oldDevAddr);

                // Build join accept downlink message

                // Build the DlSettings fields that is a superposition of RX2DR and RX1DROffset field
                var dlSettings = new byte[1];

                if (loRaDevice.DesiredRX2DataRate.HasValue)
                {
                    if (request.Region.DRtoConfiguration.ContainsKey(loRaDevice.DesiredRX2DataRate.Value))
                    {
                        dlSettings[0] = (byte)((byte)loRaDevice.DesiredRX2DataRate & 0b00001111);
                    }
                    else
                    {
                        this.logger.LogError("twin RX2 DR value is not within acceptable values");
                    }
                }

                if (request.Region.IsValidRX1DROffset(loRaDevice.DesiredRX1DROffset))
                {
                    var rx1droffset = (byte)(loRaDevice.DesiredRX1DROffset << 4);
                    dlSettings[0] = (byte)(dlSettings[0] + rx1droffset);
                }
                else
                {
                    this.logger.LogError("twin RX1 offset DR value is not within acceptable values");
                }

                // The following DesiredRxDelay is different than the RxDelay to be passed to Serialize function
                // This one is a delay between TX and RX for any message to be processed by joining device
                // The field accepted by Serialize method is an indication of the delay (compared to receive time of join request)
                // of when the message Join Accept message should be sent
                var loraSpecDesiredRxDelay = RxDelay.RxDelay0;
                if (Enum.IsDefined(loRaDevice.DesiredRXDelay))
                {
                    loraSpecDesiredRxDelay = loRaDevice.DesiredRXDelay;
                }
                else
                {
                    this.logger.LogError("twin RX delay value is not within acceptable values");
                }

                var loRaPayloadJoinAccept = new LoRaPayloadJoinAccept(
                    netId,   // NETID 0 / 1 is default test
                    devAddr, // todo add device address management
                    appNonce,
                    dlSettings,
                    loraSpecDesiredRxDelay,
                    null);

                if (!loraRegion.TryGetDownstreamChannelFrequency(request.RadioMetadata.Frequency, upstreamDataRate: request.RadioMetadata.DataRate, deviceJoinInfo: deviceJoinInfo, downstreamFrequency: out var freq))
                {
                    this.logger.LogError("could not resolve DR and/or frequency for downstream");
                    request.NotifyFailed(loRaDevice, LoRaDeviceRequestFailedReason.InvalidUpstreamMessage);
                    return;
                }

                var joinAcceptBytes = loRaPayloadJoinAccept.Serialize(appKey);

                var rx1 = windowToUse is not ReceiveWindow2
                        ? new ReceiveWindow(loraRegion.GetDownstreamDataRate(request.RadioMetadata.DataRate, loRaDevice.ReportedRX1DROffset), freq)
                        : (ReceiveWindow?)null;

                var rx2 = new ReceiveWindow(loraRegion.GetDownstreamRX2DataRate(this.configuration.Rx2DataRate, null, deviceJoinInfo, this.logger),
                                            loraRegion.GetDownstreamRX2Freq(this.configuration.Rx2Frequency, deviceJoinInfo, this.logger));

                var downlinkMessage = new DownlinkMessage(joinAcceptBytes,
                                                          request.RadioMetadata.UpInfo.Xtime,
                                                          rx1,
                                                          rx2,
                                                          loRaDevice.DevEUI,
                                                          loraRegion.JoinAcceptDelay1,
                                                          loRaDevice.ClassType,
                                                          request.StationEui,
                                                          request.RadioMetadata.UpInfo.AntennaPreference);

                this.receiveWindowHits?.Add(1, KeyValuePair.Create(MetricRegistry.ReceiveWindowTagName, (object)windowToUse));
                _ = request.DownstreamMessageSender.SendDownstreamAsync(downlinkMessage);
                request.NotifySucceeded(loRaDevice, downlinkMessage);

                if (this.logger.IsEnabled(LogLevel.Debug))
                {
                    var jsonMsg = JsonConvert.SerializeObject(downlinkMessage);
                    this.logger.LogDebug($"{MacMessageType.JoinAccept} {jsonMsg}");
                }
                else
                {
                    this.logger.LogInformation("join accepted");
                }
            }
            catch (Exception ex) when
                (ExceptionFilterUtility.True(() => this.logger.LogError(ex, $"failed to handle join request. {ex.Message}", LogLevel.Error),
                                             () => this.unhandledExceptionCount?.Add(1)))
            {
                request.NotifyFailed(loRaDevice, ex);
                throw;
            }
        }
        public async Task <IActionResult> GetDevice(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            if (req is null)
            {
                throw new ArgumentNullException(nameof(req));
            }

            try
            {
                VersionValidator.Validate(req);
            }
            catch (IncompatibleVersionException ex)
            {
                return(new BadRequestObjectResult(ex.Message));
            }

            // ABP parameters
            string devAddrString = req.Query["DevAddr"];
            // OTAA parameters
            string rawDevEui   = req.Query["DevEUI"];
            string rawDevNonce = req.Query["DevNonce"];
            var    gatewayId   = req.Query["GatewayId"];

            DevEui?devEui = null;

            if (!string.IsNullOrEmpty(rawDevEui))
            {
                if (DevEui.TryParse(rawDevEui, EuiParseOptions.ForbidInvalid, out var parsedDevEui))
                {
                    devEui = parsedDevEui;
                }
                else
                {
                    return(new BadRequestObjectResult("Dev EUI is invalid."));
                }
            }

            try
            {
                DevNonce?devNonce = ushort.TryParse(rawDevNonce, NumberStyles.None, CultureInfo.InvariantCulture, out var d) ? new DevNonce(d) : null;
                DevAddr? devAddr  = DevAddr.TryParse(devAddrString, out var someDevAddr) ? someDevAddr : null;
                var      results  = await GetDeviceList(devEui, gatewayId, devNonce, devAddr, log);

                var json = JsonConvert.SerializeObject(results);
                return(new OkObjectResult(json));
            }
            catch (DeviceNonceUsedException)
            {
                return(new BadRequestObjectResult("UsedDevNonce"));
            }
            catch (JoinRefusedException ex)
            {
                log.LogDebug("Join refused: {msg}", ex.Message);
                return(new BadRequestObjectResult("JoinRefused: " + ex.Message));
            }
            catch (ArgumentException ex)
            {
                return(new BadRequestObjectResult(ex.Message));
            }
        }
        public async Task When_OTAA_Join_Then_Sends_Upstream_DirectMethod_Should_Send_Downstream(string deviceGatewayID)
        {
            var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: deviceGatewayID));

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

            AppSessionKey?    savedAppSKey = null;
            NetworkSessionKey?savedNwkSKey = null;
            var savedDevAddr = string.Empty;

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

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

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

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

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

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

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

            var payloadata = simDevice.CreateJoinRequest();

            using var joinRequest = CreateWaitableRequest(payloadata);
            joinRequest.SetStationEui(new StationEui(ulong.MaxValue));
            messageDispatcher.DispatchRequest(joinRequest);
            Assert.True(await joinRequest.WaitCompleteAsync());
            Assert.True(joinRequest.ProcessingSucceeded);

            Assert.NotNull(savedAppSKey);
            Assert.NotNull(savedNwkSKey);
            Assert.NotEmpty(savedDevAddr);
            simDevice.SetupJoin(savedAppSKey.Value, savedNwkSKey.Value, DevAddr.Parse(savedDevAddr));
            using var request = CreateWaitableRequest(simDevice.CreateUnconfirmedDataUpMessage("1"));
            request.SetStationEui(new StationEui(ulong.MaxValue));
            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.True(request.ProcessingSucceeded);

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

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

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

            Assert.True(await classCSender.SendAsync(c2d));
            Assert.Equal(2, DownstreamMessageSender.DownlinkMessages.Count);
            var downstreamMsg = DownstreamMessageSender.DownlinkMessages[1];

            TestLogger.Log($"appSKey: {simDevice.AppSKey}, nwkSKey: {simDevice.NwkSKey}");

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

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

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

            LoRaDeviceApi.VerifyAll();
            LoRaDeviceClient.VerifyAll();
        }
示例#24
0
        public async Task When_Has_Custom_RX2DR_Should_Send_Correctly()
        {
            var devAddr   = new DevAddr(0x023637F8);
            var appSKey   = TestKeys.CreateAppSessionKey(0xABC0200000000000, 0x09);
            var nwkSKey   = TestKeys.CreateNetworkSessionKey(0xABC0200000000000, 0x09);
            var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: ServerGatewayID));
            var devEUI    = simDevice.DevEUI;

            simDevice.SetupJoin(appSKey, nwkSKey, devAddr);

            this.deviceApi.Setup(x => x.GetPrimaryKeyByEuiAsync(devEUI))
            .ReturnsAsync("123");

            var twin = simDevice.CreateOTAATwin(
                desiredProperties: new Dictionary <string, object>
            {
                { TwinProperty.RX2DataRate, "10" }
            },
                reportedProperties: new Dictionary <string, object>
            {
                { TwinProperty.RX2DataRate, 10 },
                { TwinProperty.Region, LoRaRegionType.US915.ToString() },
                // OTAA device, already joined
                { TwinProperty.DevAddr, devAddr.ToString() },
                { TwinProperty.AppSKey, appSKey.ToString() },
                { TwinProperty.NwkSKey, nwkSKey.ToString() },
                { TwinProperty.LastProcessingStationEui, new StationEui(ulong.MaxValue).ToString() }
            });

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

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

            DownlinkMessage receivedDownlinkMessage = null;

            this.downstreamMessageSender.Setup(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkMessage>()))
            .Returns(Task.CompletedTask)
            .Callback <DownlinkMessage>(d => receivedDownlinkMessage = d);

            var target = new DefaultClassCDevicesMessageSender(
                this.serverConfiguration,
                this.loRaDeviceRegistry,
                this.downstreamMessageSender.Object,
                this.frameCounterStrategyProvider,
                NullLogger <DefaultClassCDevicesMessageSender> .Instance,
                TestMeter.Instance);

            Assert.True(await target.SendAsync(c2dToDeviceMessage));

            this.downstreamMessageSender.Verify(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkMessage>()), Times.Once());

            EnsureDownlinkIsCorrect(receivedDownlinkMessage, simDevice, c2dToDeviceMessage);
            Assert.Equal(DataRateIndex.DR10, receivedDownlinkMessage.Rx2.DataRate);
            Assert.Equal(Hertz.Mega(923.3), receivedDownlinkMessage.Rx2.Frequency);

            this.downstreamMessageSender.VerifyAll();
            this.deviceApi.VerifyAll();
            this.deviceClient.VerifyAll();
        }
示例#25
0
        public override void SetupTestDevices()
        {
            var gatewayID = Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID") ?? Configuration.LeafDeviceGatewayID;

            // Simulated devices start at 1000

            // Device1001_Simulated_ABP: used for ABP simulator
            Device1001_Simulated_ABP = new TestDeviceInfo()
            {
                DeviceID       = "0000000000001001",
                GatewayID      = gatewayID,
                SensorDecoder  = "DecoderValueSensor",
                IsIoTHubDevice = true,
                AppSKey        = GetAppSessionKey(1001),
                NwkSKey        = GetNetworkSessionKey(1001),
                DevAddr        = new DevAddr(0x00001001),
            };

            // Device1002_Simulated_OTAA: used for simulator
            Device1002_Simulated_OTAA = new TestDeviceInfo()
            {
                DeviceID       = "0000000000001002",
                AppEui         = JoinEui.Parse("0000000000001002"),
                AppKey         = GetAppKey(1002),
                GatewayID      = gatewayID,
                IsIoTHubDevice = true,
                SensorDecoder  = "DecoderValueSensor",
            };

            var fileName   = "EU863.json";
            var jsonString = File.ReadAllText(fileName);

            for (var deviceID = 5000; deviceID < 5000 + Configuration.NumberOfLoadTestConcentrators; deviceID++)
            {
                this.deviceRange5000_BasicsStationSimulators.Add(new TestDeviceInfo
                {
                    DeviceID       = deviceID.ToString("0000000000000000", CultureInfo.InvariantCulture),
                    RouterConfig   = JObject.Parse(jsonString),
                    IsIoTHubDevice = true,
                });
            }

            for (var deviceId = 1100; deviceId < 1105; deviceId++)
            {
                this.deviceRange1000_ABP.Add(CreateAbpDevice(deviceId));
            }

            for (var deviceId = 2000; deviceId < 2000 + Configuration.NumberOfLoadTestDevices; deviceId++)
            {
                this.deviceRange2000_ABP_FullLoad.Add(CreateAbpDevice(deviceId));
            }

            for (var deviceId = 3000; deviceId < 3000 + Configuration.NumberOfLoadTestDevices; deviceId++)
            {
                this.deviceRange3000_OTAA_FullLoad.Add(CreateOtaaDevice(deviceId));
            }

            for (var deviceId = 4000; deviceId < 4000 + Configuration.NumberOfLoadTestDevices; deviceId++)
            {
                this.deviceRange4000_OTAA_FullLoad.Add(CreateOtaaDevice(deviceId));
            }

            DeviceRange6000_OTAA_FullLoad =
                Enumerable.Range(6000, Configuration.NumberOfLoadTestDevices)
                .Select(deviceId => CreateOtaaDevice(deviceId))
                .ToList();

            DeviceRange9000_OTAA_FullLoad_DuplicationDrop =
                Enumerable.Range(9000, Configuration.NumberOfLoadTestDevices)
                .Select(deviceId => CreateOtaaDevice(deviceId, deduplicationMode: DeduplicationMode.Drop))
                .ToList();

            TestDeviceInfo CreateAbpDevice(int deviceId) =>
            new TestDeviceInfo
            {
                DeviceID       = deviceId.ToString("0000000000000000", CultureInfo.InvariantCulture),
                AppEui         = JoinEui.Parse(deviceId.ToString("0000000000000000", CultureInfo.InvariantCulture)),
                AppKey         = GetAppKey(deviceId),
                GatewayID      = gatewayID,
                IsIoTHubDevice = true,
                SensorDecoder  = "DecoderValueSensor",
                AppSKey        = GetAppSessionKey(deviceId),
                NwkSKey        = GetNetworkSessionKey(deviceId),
                DevAddr        = DevAddr.Parse(deviceId.ToString("00000000", CultureInfo.InvariantCulture)),
            };

            TestDeviceInfo CreateOtaaDevice(int deviceId, DeduplicationMode deduplicationMode = DeduplicationMode.None) =>
            new TestDeviceInfo
            {
                DeviceID       = deviceId.ToString("0000000000000000", CultureInfo.InvariantCulture),
                AppEui         = JoinEui.Parse(deviceId.ToString("0000000000000000", CultureInfo.InvariantCulture)),
                AppKey         = GetAppKey(deviceId),
                IsIoTHubDevice = true,
                SensorDecoder  = "DecoderValueSensor",
                Deduplication  = deduplicationMode
            };
        }
        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();
        }
示例#27
0
        public static bool TryRead <T>(this TwinCollection twinCollection, string property, ILogger?logger, [NotNullWhen(true)] out T?value)
        {
            _ = twinCollection ?? throw new ArgumentNullException(nameof(twinCollection));

            value = default;

            if (!twinCollection.Contains(property))
            {
                return(false);
            }

            // cast to object to avoid dynamic code to be generated
            var some = (object)twinCollection[property];

            // quick path for values that can be directly converted
            if (some is Newtonsoft.Json.Linq.JValue someJValue)
            {
                if (someJValue.Value is T someT)
                {
                    value = someT;
                    return(true);
                }
            }

            try
            {
                var t      = typeof(T);
                var tPrime = Nullable.GetUnderlyingType(t) ?? t;

                // For 100% case coverage we should handle the case where type T is nullable and the token is null.
                // Since this is not possible in IoT hub, we do not handle the null cases exhaustively.

                if (tPrime == StationEuiType)
                {
                    value = (T)(object)StationEui.Parse(some.ToString());
                }
                else if (tPrime == DevNonceType)
                {
                    value = (T)(object)new DevNonce(Convert.ToUInt16(some, CultureInfo.InvariantCulture));
                }
                else if (tPrime == DevAddrType)
                {
                    value = (T)(object)DevAddr.Parse(some.ToString());
                }
                else if (tPrime == AppSessionKeyType)
                {
                    value = (T)(object)AppSessionKey.Parse(some.ToString());
                }
                else if (tPrime == AppKeyType)
                {
                    value = (T)(object)AppKey.Parse(some.ToString());
                }
                else if (tPrime == NetworkSessionKeyType)
                {
                    value = (T)(object)NetworkSessionKey.Parse(some.ToString());
                }
                else if (tPrime == JoinEuiType)
                {
                    value = (T)(object)JoinEui.Parse(some.ToString());
                }
                else if (tPrime == NetIdType)
                {
                    value = (T)(object)NetId.Parse(some.ToString());
                }
                else
                {
                    value = (T)Convert.ChangeType(some, t, CultureInfo.InvariantCulture);
                }
                if (t.IsEnum && !t.IsEnumDefined(value))
                {
                    LogParsingError(logger, property, some);
                    return(false);
                }
            }
            catch (Exception ex) when(ex is ArgumentException
                                      or InvalidCastException
                                      or FormatException
                                      or OverflowException
                                      or Newtonsoft.Json.JsonSerializationException)
            {
                LogParsingError(logger, property, some, ex);
                return(false);
            }
            return(true);
        }
示例#28
0
 /// <summary>
 /// Method to take a lock when querying IoT Hub for a primary key.
 /// It is blocking as only one should access it.
 /// </summary>
 public async Task <bool> TryTakeDevAddrUpdateLock(DevAddr devAddr)
 {
     return(await this.cacheStore.LockTakeAsync(string.Concat(DevAddrLockName, devAddr), this.lockOwner, DefaultSingleLockExpiry, block : true));
 }
示例#29
0
 public bool ReleaseDevAddrUpdateLock(DevAddr devAddr)
 {
     return(this.cacheStore.LockRelease(string.Concat(DevAddrLockName, devAddr), this.lockOwner));
 }
 private static string GetDevLoaderCacheKey(DevAddr devAddr) => string.Concat("devaddrloader:", devAddr);