/// <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);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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);
        }
Exemple #5
0
 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));
Exemple #7
0
 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));
     }
 }
Exemple #8
0
        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);
        }
Exemple #9
0
 public void TestMaxPayloadLength(Region region, DataRateIndex datarate, uint maxPyldSize)
 {
     Assert.Equal(region.GetMaxPayloadSize(datarate), maxPyldSize);
 }
Exemple #10
0
        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);
        }
Exemple #11
0
        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);
        }
Exemple #12
0
 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));
 }
Exemple #14
0
        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);
        }
Exemple #16
0
 public override bool TryGetDownstreamChannelFrequency(Hertz upstreamFrequency, DataRateIndex upstreamDataRate, DeviceJoinInfo deviceJoinInfo, out Hertz downstreamFrequency) =>
 throw new NotImplementedException();
Exemple #17
0
        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);
 }
Exemple #20
0
        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);
        }