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(); }
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()); }
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); }
// 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)
/// <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); }
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)); }
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); })); }
public async Task When_StationEui_Missing_Should_Fail() { var devAddr = new DevAddr(0x023637F8); var appSKey = TestKeys.CreateAppSessionKey(0xABC0200000000000, 0x09); var nwkSKey = TestKeys.CreateNetworkSessionKey(0xABC0200000000000, 0x09); var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: ServerGatewayID)); var devEUI = simDevice.DevEUI; simDevice.SetupJoin(appSKey, nwkSKey, devAddr); this.deviceApi.Setup(x => x.GetPrimaryKeyByEuiAsync(devEUI)) .ReturnsAsync("123"); var twin = simDevice.CreateOTAATwin( desiredProperties: new Dictionary <string, object> { { TwinProperty.RX2DataRate, "10" } }, reportedProperties: new Dictionary <string, object> { { TwinProperty.RX2DataRate, 10 }, { TwinProperty.Region, LoRaRegionType.US915.ToString() }, // OTAA device, already joined { TwinProperty.DevAddr, devAddr.ToString() }, { TwinProperty.AppSKey, appSKey.ToString() }, { TwinProperty.NwkSKey, nwkSKey.ToString() }, }); this.deviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(twin); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = TestPort, MessageId = Guid.NewGuid().ToString(), }; var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.downstreamMessageSender.Object, this.frameCounterStrategyProvider, NullLogger <DefaultClassCDevicesMessageSender> .Instance, TestMeter.Instance); Assert.False(await target.SendAsync(c2dToDeviceMessage)); this.downstreamMessageSender.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public 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>(); }
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; }
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); }
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(); }
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()); }
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); }
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)); }
private static string GenerateKey(DevAddr devAddr) => CacheKeyPrefix + devAddr;
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(); }
public async Task When_Has_Custom_RX2DR_Should_Send_Correctly() { var devAddr = new DevAddr(0x023637F8); var appSKey = TestKeys.CreateAppSessionKey(0xABC0200000000000, 0x09); var nwkSKey = TestKeys.CreateNetworkSessionKey(0xABC0200000000000, 0x09); var simDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, deviceClassType: 'c', gatewayID: ServerGatewayID)); var devEUI = simDevice.DevEUI; simDevice.SetupJoin(appSKey, nwkSKey, devAddr); this.deviceApi.Setup(x => x.GetPrimaryKeyByEuiAsync(devEUI)) .ReturnsAsync("123"); var twin = simDevice.CreateOTAATwin( desiredProperties: new Dictionary <string, object> { { TwinProperty.RX2DataRate, "10" } }, reportedProperties: new Dictionary <string, object> { { TwinProperty.RX2DataRate, 10 }, { TwinProperty.Region, LoRaRegionType.US915.ToString() }, // OTAA device, already joined { TwinProperty.DevAddr, devAddr.ToString() }, { TwinProperty.AppSKey, appSKey.ToString() }, { TwinProperty.NwkSKey, nwkSKey.ToString() }, { TwinProperty.LastProcessingStationEui, new StationEui(ulong.MaxValue).ToString() } }); this.deviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)) .ReturnsAsync(twin); var c2dToDeviceMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = "hello", DevEUI = devEUI, Fport = TestPort, MessageId = Guid.NewGuid().ToString(), }; DownlinkMessage receivedDownlinkMessage = null; this.downstreamMessageSender.Setup(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkMessage>())) .Returns(Task.CompletedTask) .Callback <DownlinkMessage>(d => receivedDownlinkMessage = d); var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.downstreamMessageSender.Object, this.frameCounterStrategyProvider, NullLogger <DefaultClassCDevicesMessageSender> .Instance, TestMeter.Instance); Assert.True(await target.SendAsync(c2dToDeviceMessage)); this.downstreamMessageSender.Verify(x => x.SendDownstreamAsync(It.IsNotNull <DownlinkMessage>()), Times.Once()); EnsureDownlinkIsCorrect(receivedDownlinkMessage, simDevice, c2dToDeviceMessage); Assert.Equal(DataRateIndex.DR10, receivedDownlinkMessage.Rx2.DataRate); Assert.Equal(Hertz.Mega(923.3), receivedDownlinkMessage.Rx2.Frequency); this.downstreamMessageSender.VerifyAll(); this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public 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(); }
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); }
/// <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)); }
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);