/// <summary> /// Initializes a new instance of the <see cref="ConfigureRadioStackRequest"/> class. /// </summary> /// <param name="dataRateIndex">Default Data Rate Index.</param> /// <param name="adaptiveDataRateEnabled">Adaptive Data Rate enabled/disabled.</param> /// <param name="dutyCycleControlEnabled">Duty Cycle Control enabled/disabled. (Customer Mode Required).</param> /// <param name="isClassC">Class indicator. Class A if false, Class C device if true.</param> /// <param name="extendedOutputFormatEnabled">Extended RF packet output format: Tx/Rx channel info attached if true.</param> /// <param name="rxMacForwardingEnabled">Rx MAC Command Forwarding enabled/disabled.</param> /// <param name="powerSavingEnabled">Power Saving. Off is false, automatic if true.</param> /// <param name="retryAttemps">Maximum number of retries for a reliable radio packet (parameter range: 0 to 254).</param> /// <param name="headerMacCmdCapacity">Maximum length of the MAC commands to be piggybacked in the header (parameter range: 0 to 15).</param> public ConfigureRadioStackRequest( DataRateIndex dataRateIndex, bool adaptiveDataRateEnabled = false, bool dutyCycleControlEnabled = true, bool isClassC = true, bool extendedOutputFormatEnabled = true, bool rxMacForwardingEnabled = true, bool powerSavingEnabled = false, byte retryAttemps = 10, byte headerMacCmdCapacity = 15) : base(LoRaWanMessageIdentifier.SetRadioStackRequest, new List <byte>()) { var dataRateByte = (byte)dataRateIndex; byte trxLevel = 0x10; // 16 in dec var powerSavingByte = powerSavingEnabled ? (byte)0x01 : (byte)0x00; var optionsByte = BuildOptionsByte(adaptiveDataRateEnabled, dutyCycleControlEnabled, isClassC, extendedOutputFormatEnabled, rxMacForwardingEnabled); byte bandIndex = 0x01; // Doesn't seem to work with any other value than 1. Could be a bug, but I can't be assed to investigate. Or maybe something with regions... Payload !.Add(dataRateByte); Payload !.Add(trxLevel); Payload !.Add(optionsByte); Payload !.Add(powerSavingByte); Payload !.Add(retryAttemps); Payload !.Add(bandIndex); Payload !.Add(headerMacCmdCapacity); }
public void TestDownstreamFrequency(Region region, Hertz inputFrequency, DataRateIndex inputDataRate, Hertz outputFreq, int?joinChannel = null) { var deviceJoinInfo = new DeviceJoinInfo(joinChannel); Assert.True(region.TryGetDownstreamChannelFrequency(inputFrequency, inputDataRate, deviceJoinInfo, out var frequency)); Assert.Equal(frequency, outputFreq); }
public LoRaADRResult ComputeResult(LoRaADRTable table, float requiredSnr, DataRateIndex upstreamDataRate, int minTxPower, DataRateIndex maxDr) { // We do not have enough frame to calculate ADR. We can assume that a crash was the cause. if (table == null || table.Entries.Count < 20) { this.logger.LogDebug("ADR: not enough frames captured. Sending default power values"); return(null); } // This is the first contact case to harmonize the txpower state between device and server or the crash case. if (!table.CurrentNbRep.HasValue || !table.CurrentTxPower.HasValue) { this.logger.LogDebug("ADR: Sending the device default power values"); return(null); } // This is a case of standard ADR calculus. var newNbRep = ComputeNbRepetion(table.Entries[0].FCnt, table.Entries[LoRaADRTable.FrameCountCaptureCount - 1].FCnt, table.CurrentNbRep.GetValueOrDefault()); (var newTxPowerIndex, var newDatarate) = GetPowerAndDRConfiguration(requiredSnr, upstreamDataRate, table.Entries.Max(x => x.Snr), table.CurrentTxPower.GetValueOrDefault(), minTxPower, maxDr); if (newNbRep != table.CurrentNbRep || newTxPowerIndex != table.CurrentTxPower || newDatarate != upstreamDataRate) { return(new LoRaADRResult() { DataRate = newDatarate, NbRepetition = newNbRep, TxPower = newTxPowerIndex }); } return(null); }
public void TestRegionLimit(Region region, Hertz inputFrequency, DataRateIndex datarate, int?joinChannel = null) { var deviceJoinInfo = new DeviceJoinInfo(joinChannel); var ex = Assert.Throws <LoRaProcessingException>(() => region.TryGetDownstreamChannelFrequency(inputFrequency, datarate, deviceJoinInfo, out _)); Assert.Equal(LoRaProcessingErrorCode.InvalidFrequency, ex.ErrorCode); ex = Assert.Throws <LoRaProcessingException>(() => region.GetDownstreamDataRate(datarate)); Assert.Equal(LoRaProcessingErrorCode.InvalidDataRate, ex.ErrorCode); }
private static LoRaADRResult ReturnDefaultValues(DataRateIndex upstreamDataRate, int defaultNbRep, int maxTxPowerIndex) { return(new LoRaADRResult { DataRate = upstreamDataRate, NbRepetition = defaultNbRep, TxPower = maxTxPowerIndex }); }
public static RadioMetadata GenerateTestRadioMetadata( DataRateIndex dataRate = DataRateIndex.DR2, Hertz?frequency = null, uint antennaPreference = 1, ulong xtime = 100000, uint gpstime = 100000, double rssi = 2.0, float snr = 0.1f) => new RadioMetadata(dataRate, frequency ?? Hertz.Mega(868.3), new RadioMetadataUpInfo(antennaPreference, xtime, gpstime, rssi, snr));
public void TestIsDRIndexWithinAcceptableValues(Region region, DataRateIndex datarate, bool upstream, bool isValid) { if (upstream) { Assert.Equal(isValid, region.RegionLimits.IsCurrentUpstreamDRIndexWithinAcceptableValue(datarate)); } else { Assert.Equal(isValid, region.RegionLimits.IsCurrentDownstreamDRIndexWithinAcceptableValue(datarate)); } }
public void TestDownstreamRX2DataRate(Region region, DataRateIndex?nwksrvrx2dr, DataRateIndex?rx2drfromtwins, DataRateIndex expectedDr, int?reportedJoinChannel = null, int?desiredJoinChannel = null) { var deviceJoinInfo = new DeviceJoinInfo(reportedJoinChannel, desiredJoinChannel); var datr = region.GetDownstreamRX2DataRate(nwksrvrx2dr, rx2drfromtwins, deviceJoinInfo, NullLogger.Instance); Assert.Equal(expectedDr, datr); }
public void TestMaxPayloadLength(Region region, DataRateIndex datarate, uint maxPyldSize) { Assert.Equal(region.GetMaxPayloadSize(datarate), maxPyldSize); }
public async Task Perform_Rate_Adapatation_When_Possible(uint deviceId, int count, DataRateIndex expectedDR, int expectedTXPower, int expectedNbRep, uint initialDeviceFcntUp) { uint payloadFcnt = 0; const uint InitialDeviceFcntDown = 0; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(deviceId, gatewayID: ServerConfiguration.GatewayID), frmCntUp: initialDeviceFcntUp); var loraDevice = CreateLoRaDevice(simulatedDevice); LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loraDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); // In this case we want to simulate a cache failure, so we don't initialize the cache. if (deviceId != 12) { payloadFcnt = await InitializeCacheToDefaultValuesAsync(payloadFcnt, simulatedDevice, messageProcessor); } else { var payloadInt = simulatedDevice.CreateConfirmedDataUpMessage("1234", fcnt: payloadFcnt); using var requestInt = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payloadInt); messageProcessor.DispatchRequest(requestInt); Assert.True(await requestInt.WaitCompleteAsync(-1)); payloadFcnt++; } for (var i = 0; i < count; i++) { var payloadInt = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrlFlags: FrameControlFlags.Adr); using var requestInt = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payloadInt); messageProcessor.DispatchRequest(requestInt); Assert.True(await requestInt.WaitCompleteAsync(-1)); payloadFcnt++; } var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrlFlags: FrameControlFlags.AdrAckReq | FrameControlFlags.Adr); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), payload); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); Assert.NotNull(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); Assert.Equal(2, DownstreamMessageSender.DownlinkMessages.Count); var downlinkMessage = DownstreamMessageSender.DownlinkMessages[1]; var payloadDataDown = new LoRaPayloadData(downlinkMessage.Data); // in this case we expect a null payload if (deviceId == 11) { Assert.Equal(0, payloadDataDown.Frmpayload.Span.Length); } else { // We expect a mac command in the payload Assert.Equal(5, payloadDataDown.Frmpayload.Span.Length); var decryptedPayload = payloadDataDown.Serialize(simulatedDevice.NwkSKey.Value); Assert.Equal(FramePort.MacCommand, payloadDataDown.Fport); Assert.Equal((byte)Cid.LinkADRCmd, decryptedPayload[0]); var linkAdr = new LinkADRRequest(decryptedPayload); Assert.Equal(expectedDR, linkAdr.DataRate); Assert.Equal(expectedDR, loraDevice.DataRate); Assert.Equal(expectedTXPower, linkAdr.TxPower); Assert.Equal(expectedTXPower, loraDevice.TxPower); Assert.Equal(expectedNbRep, linkAdr.NbRep); Assert.Equal(expectedNbRep, loraDevice.NbRep); } // in case no payload the mac is in the FRMPayload and is decrypted with NwkSKey Assert.Equal(payloadDataDown.DevAddr, loraDevice.DevAddr); Assert.False(payloadDataDown.IsConfirmed); Assert.Equal(MacMessageType.UnconfirmedDataDown, payloadDataDown.MessageType); // 4. Frame counter up was updated Assert.Equal(payloadFcnt, loraDevice.FCntUp); // 5. Frame counter down is updated Assert.Equal(InitialDeviceFcntDown + 1, loraDevice.FCntDown); Assert.Equal(InitialDeviceFcntDown + 1, payloadDataDown.Fcnt); }
public void GetDownstreamDataRate_ThrowsWhenOffsetInvalid(Region region, DataRateIndex inputDataRate, int rx1DrOffset) { var ex = Assert.Throws <LoRaProcessingException>(() => region.GetDownstreamDataRate(inputDataRate, rx1DrOffset)); Assert.Equal(LoRaProcessingErrorCode.InvalidDataRateOffset, ex.ErrorCode); }
public void TestDownstreamDataRate(Region region, DataRateIndex inputDataRate, DataRateIndex outputDr, int rx1DrOffset = 0) { Assert.Equal(region.GetDownstreamDataRate(inputDataRate, rx1DrOffset), outputDr); }
public override Task <LoRaADRResult> CalculateADRResultAndAddEntryAsync(DevEui devEUI, string gatewayId, uint fCntUp, uint fCntDown, float requiredSnr, DataRateIndex dataRate, int minTxPower, DataRateIndex maxDr, LoRaADRTableEntry newEntry = null) { return(Task.FromResult <LoRaADRResult>(null)); }
public async Task Perform_DR_Adaptation_When_Needed(uint deviceId, float currentLsnr, string currentDRString, DataRateIndex expectedDR, int expectedTxPower) { var currentDR = LoRaDataRate.Parse(currentDRString); var messageCount = 21; uint payloadFcnt = 0; const uint InitialDeviceFcntUp = 9; const uint ExpectedDeviceFcntDown = 0; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(deviceId, gatewayID: ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp); var loraDevice = CreateLoRaDevice(simulatedDevice); LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); var twinDR = DR0; var twinTxPower = 0; LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .Callback <TwinCollection, CancellationToken>((t, _) => { if (t.Contains(TwinProperty.DataRate)) { twinDR = t[TwinProperty.DataRate]; } if (t.Contains(TwinProperty.TxPower)) { twinTxPower = (int)t[TwinProperty.TxPower]; } }) .ReturnsAsync(true); using var cache = EmptyMemoryCache(); using var loraDeviceCache = CreateDeviceCache(loraDevice); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); payloadFcnt = await InitializeCacheToDefaultValuesAsync(payloadFcnt, simulatedDevice, messageProcessor); for (var i = 0; i < messageCount; i++) { payloadFcnt = await SendMessage(currentLsnr, currentDR, payloadFcnt, simulatedDevice, messageProcessor, FrameControlFlags.Adr); } var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrlFlags: FrameControlFlags.AdrAckReq | FrameControlFlags.Adr); var radioMetadata = new RadioMetadata(TestUtils.TestRegion.GetDataRateIndex(currentDR), Hertz.Mega(868.1), new RadioMetadataUpInfo(0, 0, 0, 0, currentLsnr)); using var request = CreateWaitableRequest(radioMetadata, payload); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub LoRaDeviceClient.VerifyAll(); LoRaDeviceApi.VerifyAll(); Assert.True(request.ProcessingSucceeded); Assert.NotNull(request.ResponseDownlink); var downlinkMessage = DownstreamMessageSender.DownlinkMessages[1]; var payloadDataDown = new LoRaPayloadData(downlinkMessage.Data); // We expect a mac command in the payload if (deviceId == 221) { // In this case, no ADR adaptation is performed, so the message should be empty Assert.Equal(0, payloadDataDown.Frmpayload.Span.Length); Assert.Equal(0, payloadDataDown.Fopts.Span.Length); } else { Assert.Equal(5, payloadDataDown.Frmpayload.Span.Length); var decryptedPayload = payloadDataDown.Serialize(simulatedDevice.NwkSKey.Value); Assert.Equal(FramePort.MacCommand, payloadDataDown.Fport); Assert.Equal((byte)Cid.LinkADRCmd, decryptedPayload[0]); var linkAdr = new LinkADRRequest(decryptedPayload); Assert.Equal(expectedDR, linkAdr.DataRate); Assert.Equal(expectedDR, loraDevice.DataRate); Assert.Equal(expectedDR, twinDR); Assert.Equal(expectedTxPower, linkAdr.TxPower); Assert.Equal(expectedTxPower, loraDevice.TxPower); Assert.Equal(expectedTxPower, twinTxPower); // in case no payload the mac is in the FRMPayload and is decrypted with NwkSKey Assert.Equal(payloadDataDown.DevAddr, loraDevice.DevAddr); Assert.False(payloadDataDown.IsConfirmed); Assert.Equal(MacMessageType.UnconfirmedDataDown, payloadDataDown.MessageType); // 4. Frame counter up was updated Assert.Equal(payloadFcnt, loraDevice.FCntUp); // 5. Frame counter down is updated Assert.Equal(ExpectedDeviceFcntDown + 1, loraDevice.FCntDown); Assert.Equal(ExpectedDeviceFcntDown + 1, payloadDataDown.Fcnt); } }
public async Task When_Getting_DLSettings_From_Twin_Returns_JoinAccept_With_Correct_Settings(int rx1DROffset, DataRateIndex rx2datarate) { var deviceGatewayID = ServerGatewayID; var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID)); var joinRequest = simulatedDevice.CreateJoinRequest(); var devAddr = (DevAddr?)null; var devEui = simulatedDevice.LoRaDevice.DevEui; // Device twin will be queried var twin = new Twin(); twin.Properties.Desired[TwinProperty.DevEUI] = devEui.ToString(); twin.Properties.Desired[TwinProperty.AppEui] = simulatedDevice.LoRaDevice.AppEui?.ToString(); twin.Properties.Desired[TwinProperty.AppKey] = simulatedDevice.LoRaDevice.AppKey?.ToString(); twin.Properties.Desired[TwinProperty.GatewayID] = deviceGatewayID; twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder; twin.Properties.Desired[TwinProperty.RX1DROffset] = rx1DROffset; twin.Properties.Desired[TwinProperty.RX2DataRate] = rx2datarate; LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(twin); LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .ReturnsAsync(true); // Lora device api will be search by devices with matching deveui, LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequest.DevNonce)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList())); using var memoryCache = new MemoryCache(new MemoryCacheOptions()); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.NotNull(request.ResponseDownlink); Assert.Single(DownstreamMessageSender.DownlinkMessages); var downlinkMessage = DownstreamMessageSender.DownlinkMessages[0]; var joinAccept = new LoRaPayloadJoinAccept(downlinkMessage.Data, simulatedDevice.LoRaDevice.AppKey.Value); Assert.Equal(rx1DROffset, joinAccept.Rx1DrOffset); Assert.Equal(rx2datarate, joinAccept.Rx2Dr); }
public override bool TryGetDownstreamChannelFrequency(Hertz upstreamFrequency, DataRateIndex upstreamDataRate, DeviceJoinInfo deviceJoinInfo, out Hertz downstreamFrequency) => throw new NotImplementedException();
private static (int txPower, DataRateIndex datarate) GetPowerAndDRConfiguration(float requiredSnr, DataRateIndex dataRate, double maxSnr, int currentTxPowerIndex, int minTxPowerIndex, DataRateIndex maxDr) { var snrMargin = maxSnr - requiredSnr - MarginDb; var computedDataRate = dataRate; var nStep = (int)snrMargin; while (nStep != 0) { if (nStep > 0) { if (computedDataRate < maxDr) { computedDataRate++; } // txpower is an inverted scale, hence if we want to reduce else if (currentTxPowerIndex < minTxPowerIndex) { currentTxPowerIndex++; } nStep--; if (currentTxPowerIndex >= minTxPowerIndex) { return(minTxPowerIndex, computedDataRate); } } else if (nStep < 0) { if (currentTxPowerIndex > MaxTxPowerIndex) { currentTxPowerIndex--; nStep++; } else { return(currentTxPowerIndex, computedDataRate); } } } return(currentTxPowerIndex, computedDataRate); }
public async Task When_Getting_Custom_RX2_DR_From_Twin_Returns_JoinAccept_With_Correct_Settings_And_Behaves_Correctly(DataRateIndex rx2datarate) { var deviceGatewayID = ServerGatewayID; var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID)); var joinRequest = simulatedDevice.CreateJoinRequest(); AppSessionKey? afterJoinAppSKey = null; NetworkSessionKey?afterJoinNwkSKey = null; string afterJoinDevAddr = null; var afterJoinFcntDown = -1; var afterJoinFcntUp = -1; uint startingPayloadFcnt = 0; var devAddr = (DevAddr?)null; var devEui = simulatedDevice.LoRaDevice.DevEui; // 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); // Device twin will be queried var twin = new Twin(); twin.Properties.Desired[TwinProperty.DevEUI] = devEui.ToString(); twin.Properties.Desired[TwinProperty.AppEui] = simulatedDevice.LoRaDevice.AppEui?.ToString(); twin.Properties.Desired[TwinProperty.AppKey] = simulatedDevice.LoRaDevice.AppKey?.ToString(); twin.Properties.Desired[TwinProperty.GatewayID] = deviceGatewayID; twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder; twin.Properties.Desired[TwinProperty.RX2DataRate] = rx2datarate; twin.Properties.Desired[TwinProperty.PreferredWindow] = 2; LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(twin); LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>())) .Callback <TwinCollection, CancellationToken>((updatedTwin, _) => { afterJoinAppSKey = AppSessionKey.Parse(updatedTwin[TwinProperty.AppSKey].Value); afterJoinNwkSKey = NetworkSessionKey.Parse(updatedTwin[TwinProperty.NwkSKey].Value); afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr].Value; afterJoinFcntDown = updatedTwin[TwinProperty.FCntDown].Value; afterJoinFcntUp = updatedTwin[TwinProperty.FCntUp].Value; }) .ReturnsAsync(true); // Lora device api will be search by devices with matching deveui, LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequest.DevNonce)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList())); using var memoryCache = new MemoryCache(new MemoryCacheOptions()); using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache); // Send to message processor using var messageProcessor = new MessageDispatcher( ServerConfiguration, deviceRegistry, FrameCounterUpdateStrategyProvider); using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.NotNull(request.ResponseDownlink); Assert.Single(DownstreamMessageSender.DownlinkMessages); var downlinkMessage = DownstreamMessageSender.DownlinkMessages[0]; var joinAccept = new LoRaPayloadJoinAccept(downlinkMessage.Data, simulatedDevice.LoRaDevice.AppKey.Value); if (rx2datarate is > DR0 and < DR8) { Assert.Equal(rx2datarate, joinAccept.Rx2Dr); }
public void MemberValue(int expected, DataRateIndex input) { Assert.Equal(expected, (int)input); }
public virtual async Task <LoRaADRResult> CalculateADRResultAndAddEntryAsync(DevEui devEUI, string gatewayId, uint fCntUp, uint fCntDown, float requiredSnr, DataRateIndex dataRate, int minTxPower, DataRateIndex maxDr, LoRaADRTableEntry newEntry = null) { var table = newEntry != null ? await this.store.AddTableEntry(newEntry) : await this.store.GetADRTable(devEUI); var currentStrategy = this.strategyProvider.GetStrategy(); var result = currentStrategy.ComputeResult(table, requiredSnr, dataRate, minTxPower, maxDr); if (result == null) { // In this case we want to reset the device to default values as we have null values if (table == null || !table.CurrentNbRep.HasValue || !table.CurrentTxPower.HasValue || fCntUp > currentStrategy.MinimumNumberOfResult) { result = ReturnDefaultValues(dataRate, currentStrategy.DefaultNbRep, currentStrategy.DefaultTxPower); } else { result = await GetLastResultAsync(devEUI) ?? new LoRaADRResult(); result.NumberOfFrames = table.Entries.Count; return(result); } } var nextFcntDown = await NextFCntDown(devEUI, gatewayId, fCntUp, fCntDown); result.CanConfirmToDevice = nextFcntDown > 0; if (result.CanConfirmToDevice) { if (table == null) { // in a reset case, we may not have a table, but still want to store the default // values that we sent to the client table = new LoRaADRTable(); } table.CurrentNbRep = result.NbRepetition; table.CurrentTxPower = result.TxPower; await this.store.UpdateADRTable(devEUI, table); UpdateState(result); result.FCntDown = nextFcntDown; } result.NumberOfFrames = table.Entries.Count; this.logger.LogDebug($"calculated ADR: CanConfirmToDevice: {result.CanConfirmToDevice}, TxPower: {result.TxPower}, DataRate: {result.DataRate}"); return(result); }