示例#1
0
        private async Task <TwinCollection> GetTwinDesiredPropertiesAsync(StationEui stationEui, CancellationToken cancellationToken)
        {
            var cacheKey = $"{ConcentratorTwinCachePrefixName}{stationEui}";

            if (this.cache.TryGetValue(cacheKey, out var result))
            {
                return((TwinCollection)result);
            }

            await this.cacheSemaphore.WaitAsync(cancellationToken);

            try
            {
                return(await this.cache.GetOrCreateAsync(cacheKey, async cacheEntry =>
                {
                    _ = cacheEntry.SetAbsoluteExpiration(CacheTimeout);
                    var key = await this.loRaDeviceApiService.GetPrimaryKeyByEuiAsync(stationEui);
                    if (string.IsNullOrEmpty(key))
                    {
                        throw new LoRaProcessingException($"The configuration request of station '{stationEui}' did not match any configuration in IoT Hub. If you expect this connection request to succeed, make sure to provision the Basics Station in the device registry.",
                                                          LoRaProcessingErrorCode.InvalidDeviceConfiguration);
                    }

                    using var client = this.loRaDeviceFactory.CreateDeviceClient(stationEui.ToString(), key);
                    var twin = await client.GetTwinAsync(cancellationToken);
                    return twin.Properties.Desired;
                }));
            }
            finally
            {
                _ = this.cacheSemaphore.Release();
            }
        }
        internal async Task <IActionResult> RunFetchConcentratorCredentials(HttpRequest req, CancellationToken cancellationToken)
        {
            if (!StationEui.TryParse((string)req.Query["StationEui"], out var stationEui))
            {
                this.logger.LogError("StationEui missing in request or invalid");
                return(new BadRequestObjectResult("StationEui missing in request or invalid"));
            }

            var credentialTypeQueryString = req.Query["CredentialType"];

            if (StringValues.IsNullOrEmpty(credentialTypeQueryString))
            {
                this.logger.LogError("CredentialType missing in request");
                return(new BadRequestObjectResult("CredentialType missing in request"));
            }
            if (!Enum.TryParse <ConcentratorCredentialType>(credentialTypeQueryString.ToString(), out var credentialType))
            {
                this.logger.LogError("Could not parse '{QueryString}' to a ConcentratorCredentialType.", credentialTypeQueryString.ToString());
                return(new BadRequestObjectResult($"Could not parse desired concentrator credential type '{credentialTypeQueryString}'."));
            }

            var twin = await this.registryManager.GetTwinAsync(stationEui.ToString(), cancellationToken);

            if (twin != null)
            {
                this.logger.LogInformation("Retrieving '{CredentialType}' for '{StationEui}'.", credentialType.ToString(), stationEui);
                try
                {
                    if (!twin.Properties.Desired.TryReadJsonBlock(CupsPropertyName, out var cupsProperty))
                    {
                        throw new ArgumentOutOfRangeException(CupsPropertyName, "failed to read cups config");
                    }

                    var parsedJson = JObject.Parse(cupsProperty);
                    var url        = credentialType is ConcentratorCredentialType.Lns ? parsedJson[LnsCredentialsUrlPropertyName].ToString()
                                                                               : parsedJson[CupsCredentialsUrlPropertyName].ToString();
                    var result = await GetBase64EncodedBlobAsync(url, cancellationToken);

                    return(new OkObjectResult(result));
                }
                catch (Exception ex) when(ex is ArgumentOutOfRangeException
                                          or JsonReaderException
                                          or InvalidCastException
                                          or InvalidOperationException)
                {
                    var message = $"'{CupsPropertyName}' desired property was not found or misconfigured.";

                    this.logger.LogError(ex, message);
                    return(new ObjectResult(message)
                    {
                        StatusCode = (int)HttpStatusCode.InternalServerError,
                    });
                }
            }
            else
            {
                this.logger.LogInformation($"Searching for {stationEui} returned 0 devices");
                return(new NotFoundResult());
            }
        }
示例#3
0
        private static DownlinkMessage BuildDownstreamMessage(LoRaDevice loRaDevice,
                                                              StationEui stationEUI,
                                                              ILogger logger,
                                                              ulong xTime,
                                                              ReceiveWindow?rx1,
                                                              ReceiveWindow rx2,
                                                              RxDelay lnsRxDelay,
                                                              LoRaPayloadData loRaMessage,
                                                              LoRaDeviceClassType deviceClassType,
                                                              uint?antennaPreference = null)
        {
            var messageBytes    = loRaMessage.Serialize(loRaDevice.AppSKey.Value, loRaDevice.NwkSKey.Value);
            var downlinkMessage = new DownlinkMessage(
                messageBytes,
                xTime,
                rx1, rx2,
                loRaDevice.DevEUI,
                lnsRxDelay,
                deviceClassType,
                stationEUI,
                antennaPreference
                );

            if (logger.IsEnabled(LogLevel.Debug))
            {
                logger.LogDebug($"{loRaMessage.MessageType} {JsonConvert.SerializeObject(downlinkMessage)}");
            }
            return(downlinkMessage);
        }
        public DownlinkMessage(byte[] payload,
                               ulong xtime,
                               ReceiveWindow?rx1,
                               ReceiveWindow rx2,
                               DevEui devEui,
                               RxDelay lnsRxDelay,
                               LoRaDeviceClassType deviceClassType,
                               StationEui stationEui  = default,
                               uint?antennaPreference = null)
        {
            if (payload is null)
            {
                throw new ArgumentNullException(nameof(payload));
            }
            Data = payload;

            DevEui            = devEui;
            LnsRxDelay        = lnsRxDelay;
            DeviceClassType   = deviceClassType;
            AntennaPreference = antennaPreference;
            Rx1        = rx1;
            Rx2        = rx2;
            StationEui = stationEui;
            Xtime      = xtime;
        }
示例#5
0
            public void StationEui_Can_Be_Interpreted_As_Dev_Eui()
            {
                const ulong value      = 0x1a2b3c;
                var         devEui     = new DevEui(value);
                var         stationEui = new StationEui(value);

                Assert.Equal(devEui.ToString(), stationEui.ToString());
            }
示例#6
0
        public void Write_Fails_BecauseNullUri()
        {
            _ = Id6.TryParse("b827:ebff:fee1:e39a", out var stationId6);
            var stationEui = new StationEui(stationId6);

            Assert.Throws <ArgumentNullException>(() =>
                                                  Json.Write(w => LnsDiscovery.WriteResponse(w, stationEui, ValidMuxs, null)));
        }
示例#7
0
        private async Task <string> GetDesiredPropertyStringAsync(StationEui stationEui, string propertyName, CancellationToken cancellationToken)
        {
            var desiredProperties = await GetTwinDesiredPropertiesAsync(stationEui, cancellationToken);

            return(desiredProperties.TryReadJsonBlock(propertyName, out var json)
                ? json
                : throw new LoRaProcessingException($"Property '{propertyName}' was not present in device twin.", LoRaProcessingErrorCode.InvalidDeviceConfiguration));
        }
        public void ReadRequired_Nullable_Success()
        {
            const string key           = "test";
            var          expectedValue = new StationEui(1);

            var tc = CreateTwinCollectionReader(key, expectedValue.ToString());

            Assert.Equal(expectedValue, tc.ReadRequired <StationEui?>(key));
        }
示例#9
0
        public void Write_Fails_BecauseOfNonId6Muxs()
        {
            var muxs = "000000FFFE000000";

            _ = Id6.TryParse("b827:ebff:fee1:e39a", out var stationId6);
            var stationEui = new StationEui(stationId6);

            Assert.Throws <ArgumentException>(() =>
                                              _ = Json.Write(w => LnsDiscovery.WriteResponse(w, stationEui, muxs, new Uri(ValidUrlString))));
        }
示例#10
0
 public BasicsStationConfigurationServiceTests()
 {
     this.stationEui = new StationEui(ulong.MaxValue);
     this.devEui     = DevEui.Parse(this.stationEui.ToString());
     this.loRaDeviceApiServiceMock = new Mock <LoRaDeviceAPIServiceBase>();
     this.loRaDeviceFactoryMock    = new Mock <ILoRaDeviceFactory>();
     this.memoryCache = new MemoryCache(new MemoryCacheOptions());
     this.sut         = new BasicsStationConfigurationService(this.loRaDeviceApiServiceMock.Object,
                                                              this.loRaDeviceFactoryMock.Object,
                                                              this.memoryCache,
                                                              NullLogger <BasicsStationConfigurationService> .Instance);
 }
        public void GetTagsInOrder_Should_Fall_Back_To_Tag_Bag_For_Station_Eui()
        {
            // arrange
            var stationEui = new StationEui(1);
            this.metricTagBag.StationEui.Value = stationEui;

            // act
            var result = MetricExporterHelper.GetTagsInOrder(new[] { MetricRegistry.ConcentratorIdTagName }, Array.Empty<KeyValuePair<string, object?>>(), this.metricTagBag);

            // assert
            Assert.Equal(new[] { stationEui.ToString() }, result);
        }
        public SimulatedLoadTests(IntegrationTestFixtureSim testFixture, ITestOutputHelper testOutputHelper)
            : base(testFixture)
        {
            this.uniqueMessageFragment = Guid.NewGuid().ToString();
            this.logger = new TestOutputLogger(testOutputHelper);
            this.simulatedBasicsStations =
                testFixture.DeviceRange5000_BasicsStationSimulators
                .Zip(Configuration.LnsEndpointsForSimulator.Repeat(),
                     (tdi, lnsUrl) => new SimulatedBasicsStation(StationEui.Parse(tdi.DeviceID), lnsUrl))
                .ToList();

            Assert.True(this.simulatedBasicsStations.Count % Configuration.LnsEndpointsForSimulator.Count == 0, "Since Basics Stations are round-robin distributed to LNS, we must have the same number of stations per LNS for well-defined test assertions.");
        }
示例#13
0
        public async Task <bool> ValidateAsync(X509Certificate2 certificate, X509Chain?chain, SslPolicyErrors sslPolicyErrors, CancellationToken token)
        {
            if (certificate is null)
            {
                throw new ArgumentNullException(nameof(certificate));
            }
            if (chain is null)
            {
                throw new ArgumentNullException(nameof(chain));
            }

            var commonName   = certificate.GetNameInfo(X509NameType.SimpleName, false);
            var regex        = Regex.Match(commonName, "([a-fA-F0-9]{2}[-:]?){8}");
            var parseSuccess = StationEui.TryParse(regex.Value, out var stationEui);

            if (!parseSuccess)
            {
                this.logger.LogError("Could not find a possible StationEui in '{CommonName}'.", commonName);
                return(false);
            }

            using var scope = this.logger.BeginEuiScope(stationEui);

            // Logging any chain related issue that is causing verification to fail
            if (chain.ChainStatus.Any(s => s.Status != X509ChainStatusFlags.NoError))
            {
                foreach (var status in chain.ChainStatus)
                {
                    this.logger.LogError("{Status} {StatusInformation}", status.Status, status.StatusInformation);
                }
                this.logger.LogError("Some errors were found in the chain.");
                return(false);
            }

            // Additional validation is done on certificate thumprint
            try
            {
                var thumbprints = await this.stationConfigurationService.GetAllowedClientThumbprintsAsync(stationEui, token);

                var thumbprintFound = thumbprints.Any(t => t.Equals(certificate.Thumbprint, StringComparison.OrdinalIgnoreCase));
                if (!thumbprintFound)
                {
                    this.logger.LogDebug($"'{certificate.Thumbprint}' was not found as allowed thumbprint for {stationEui}");
                }
                return(thumbprintFound);
            }
            catch (Exception ex) when(ExceptionFilterUtility.False(() => this.logger.LogError(ex, "An exception occurred while processing requests: {Exception}.", ex)))
            {
                return(false);
            }
        }
示例#14
0
        public async Task <Region> GetRegionAsync(StationEui stationEui, CancellationToken cancellationToken)
        {
            var config = await GetRouterConfigMessageAsync(stationEui, cancellationToken);

            var region = LnsStationConfiguration.GetRegion(config);

            if (region is DwellTimeLimitedRegion someRegion)
            {
                var dwellTimeSettings = await GetDesiredPropertyStringAsync(stationEui, DwellTimeConfigurationPropertyName, cancellationToken);

                someRegion.DesiredDwellTimeSetting = DwellTimeConfigurationReader.Read(dwellTimeSettings);
            }
            return(region);
        }
示例#15
0
        internal async Task <IActionResult> RunFetchConcentratorFirmware(HttpRequest req, CancellationToken cancellationToken)
        {
            if (!StationEui.TryParse((string)req.Query["StationEui"], out var stationEui))
            {
                this.logger.LogError("StationEui missing in request or invalid");
                return(new BadRequestObjectResult("StationEui missing in request or invalid"));
            }

            var twin = await this.registryManager.GetTwinAsync(stationEui.ToString("N", CultureInfo.InvariantCulture), cancellationToken);

            if (twin != null)
            {
                this.logger.LogDebug("Retrieving firmware url for '{StationEui}'.", stationEui);
                try
                {
                    if (!twin.Properties.Desired.TryReadJsonBlock(CupsPropertyName, out var cupsProperty))
                    {
                        throw new ArgumentOutOfRangeException(CupsPropertyName, "Failed to read CUPS config");
                    }

                    var fwUrl = JObject.Parse(cupsProperty)[CupsFwUrlPropertyName].ToString();
                    var(fwLength, stream) = await GetBlobStreamAsync(fwUrl, cancellationToken);

                    return(new FileStreamWithContentLengthResult(stream, "application/octet-stream", fwLength));
                }
                catch (Exception ex) when(ex is ArgumentOutOfRangeException or JsonReaderException or NullReferenceException)
                {
                    var message = $"Failed to parse firmware upgrade url from the '{CupsPropertyName}' desired property.";

                    this.logger.LogError(ex, message);
                    return(new ObjectResult(message)
                    {
                        StatusCode = (int)HttpStatusCode.InternalServerError,
                    });
                }
                catch (RequestFailedException ex)
                {
                    this.logger.LogError(ex, "Failed to download firmware from storage.");
                    return(new ObjectResult("Failed to download firmware")
                    {
                        StatusCode = (int)HttpStatusCode.InternalServerError
                    });
                }
            }
            else
            {
                this.logger.LogInformation($"Searching for {stationEui} returned 0 devices");
                return(new NotFoundResult());
            }
        }
        public async Task HandleUpdateInfoAsync_Invokes_FetchFirmware_WhenUpdateAvailable()
        {
            // setup
            using var memoryStream = new MemoryStream();
            var strictifiedInput = JsonUtil.Strictify(CupsRequestJson);

            var(httpContext, httpRequest, _) = SetupHttpContextWithRequest(strictifiedInput, memoryStream);
            _ = httpRequest.Setup(r => r.ContentLength).Returns(Encoding.UTF8.GetByteCount(strictifiedInput));

            var signatureBase64 = "ABCD";
            // setting up the twin in such a way that there is a fw update but no matching checksum
            var cupsTwinInfo = new CupsTwinInfo(new Uri(CupsUri),
                                                new Uri(TcUri),
                                                CredentialsChecksum,
                                                CredentialsChecksum,
                                                string.Empty,
                                                string.Empty,
                                                "anotherVersion",
                                                KeyChecksum,
                                                signatureBase64,
                                                new Uri(FwUrl));

            _ = this.basicsStationConfigurationService.Setup(m => m.GetCupsConfigAsync(It.IsAny <StationEui>(), It.IsAny <CancellationToken>()))
                .Returns(Task.FromResult(cupsTwinInfo));

            var firmwareBytes = new byte[] { 1, 2, 3 };

            using var httpContent = new ByteArrayContent(firmwareBytes);
            _ = this.deviceAPIServiceBase.Setup(m => m.FetchStationFirmwareAsync(It.IsAny <StationEui>(), It.IsAny <CancellationToken>()))
                .ReturnsAsync(httpContent);
            // act
            await this.processor.HandleUpdateInfoAsync(httpContext.Object, default);

            // assert

            this.deviceAPIServiceBase.Verify(m => m.FetchStationFirmwareAsync(StationEui.Parse(StationEuiString), It.IsAny <CancellationToken>()), Times.Once);

            var expectedHeader = new CupsUpdateInfoResponseHeader
            {
                UpdateSignature  = Convert.FromBase64String(signatureBase64),
                SignatureKeyCrc  = KeyChecksum,
                UpdateDataLength = (uint)firmwareBytes.Length,
            };
            var expectedHeaderBytes = expectedHeader.Serialize(new byte[256].AsMemory()).ToArray();

            var response = memoryStream.ToArray();

            Assert.Equal(expectedHeaderBytes, response[..expectedHeaderBytes.Length]);
示例#17
0
        public void When_Tag_Not_Specified_Should_Fallback_To_Tag_Bag()
        {
            // arrange
            using var instrument = new Meter(MetricRegistry.Namespace, MetricRegistry.Version);
            var stationEui = new StationEui(1);

            this.registryMetricTagBag.StationEui.Value = stationEui;
            const int value   = 1;
            var       counter = instrument.CreateCounter <int>(CounterMetric.Name);

            // act
            applicationInsightsMetricExporter.Start();
            counter.Add(value);

            // assert
            this.trackValueMock.Verify(me => me.Invoke(It.IsAny <Metric>(), value, new[] { stationEui.ToString() }), Times.Once);
        }
        public void When_Tag_Not_Specified_Should_Fallback_To_Tag_Bag()
        {
            // arrange
            using var instrument = new Meter(MetricRegistry.Namespace, MetricRegistry.Version);
            var stationEui = new StationEui(1);

            this.metricTagBag.StationEui.Value = stationEui;
            const int value   = 1;
            var       counter = instrument.CreateCounter <int>(Counter.Name);

            // act
            this.prometheusMetricExporter.Start();
            counter.Add(value);

            // assert
            this.incCounterMock.Verify(me => me.Invoke(Counter.Name, new[] { stationEui.ToString() }, value), Times.Once);
        }
        private bool EnsureFirstMessageInCache(object key, LoRaRequest loRaRequest, out StationEui previousStation)
        {
            var stationEui = loRaRequest.StationEui;

            lock (cacheLock)
            {
                if (!this.cache.TryGetValue(key, out previousStation))
                {
                    _ = this.cache.Set(key, stationEui, new MemoryCacheEntryOptions()
                    {
                        SlidingExpiration = DefaultExpiration
                    });
                    return(true);
                }
            }

            return(false);
        }
        public async void When_ObservableGauge_Is_Recorded_Should_Export_To_Prometheus()
        {
            // arrange
            var observeValue = new Mock <Func <Measurement <int> > >();
            var stationEui   = new StationEui(1);
            var measurement  = new Measurement <int>(1, KeyValuePair.Create(MetricRegistry.ConcentratorIdTagName, (object?)stationEui));

            observeValue.Setup(ov => ov.Invoke()).Returns(measurement);
            using var meter = new Meter("LoRaWan", "1.0");
            _ = meter.CreateObservableGauge(ObservableGauge.Name, observeValue.Object);

            // act
            this.prometheusMetricExporter.Start();

            // assert
            await observeValue.RetryVerifyAsync(ov => ov.Invoke(), Times.Once);

            this.recordObservableGaugeMock.Verify(r => r.Invoke(ObservableGauge.Name, new[] { stationEui.ToString() }, measurement.Value), Times.Once);
        }
示例#21
0
        public async Task <string[]> GetAllowedClientThumbprintsAsync(StationEui stationEui, CancellationToken cancellationToken)
        {
            var desiredProperties = await GetTwinDesiredPropertiesAsync(stationEui, cancellationToken);

            if (desiredProperties.Contains(ClientThumbprintPropertyName))
            {
                try
                {
                    var thumbprints = (JArray)(object)desiredProperties[ClientThumbprintPropertyName];
                    return(thumbprints.ToObject <string[]>());
                }
                catch (Exception ex) when(ex is InvalidCastException)
                {
                    throw new LoRaProcessingException($"'{ClientThumbprintPropertyName}' format is invalid. An array is expected.", ex, LoRaProcessingErrorCode.InvalidDeviceConfiguration);
                }
            }

            throw new LoRaProcessingException($"Property '{ClientThumbprintPropertyName}' was not present in device twin.", LoRaProcessingErrorCode.InvalidDeviceConfiguration);
        }
        public void When_Data_Message_Encountered_Should_Find_Duplicates_For_Different_Deduplication_Strategies(string station1, string station2, DeduplicationMode deduplicationMode, ConcentratorDeduplicationResult expectedResult)
        {
            // arrange
            var station1Eui = StationEui.Parse(station1);

            this.dataRequest.SetStationEui(station1Eui);
            _ = this.concentratorDeduplication.CheckDuplicateData(this.dataRequest, this.loRaDevice);

            this.dataRequest.SetStationEui(StationEui.Parse(station2));
            this.loRaDevice.Deduplication = deduplicationMode;

            // act/assert
            Assert.Equal(expectedResult, this.concentratorDeduplication.CheckDuplicateData(this.dataRequest, this.loRaDevice));
            Assert.Equal(1, this.cache.Count);
            var key = ConcentratorDeduplication.CreateCacheKey(this.dataPayload, this.loRaDevice);

            Assert.True(this.cache.TryGetValue(key, out var foundStation));
            Assert.Equal(station1Eui, foundStation);
        }
示例#23
0
        public async void When_ObservableGauge_Is_Recorded_Should_Export_To_ApplicationInsights()
        {
            // arrange
            var observeValue = new Mock <Func <Measurement <int> > >();
            var stationEui   = new StationEui(1);
            var measurement  = new Measurement <int>(1, KeyValuePair.Create(MetricRegistry.ConcentratorIdTagName, (object)stationEui));

            _ = observeValue.Setup(ov => ov.Invoke()).Returns(measurement);
            using var meter = new Meter("LoRaWan", "1.0");
            _ = meter.CreateObservableGauge(ObservableGaugeMetric.Name, observeValue.Object);

            // act
            this.applicationInsightsMetricExporter.Start();

            // assert
            await this.trackValueMock.RetryVerifyAsync(me => me.Invoke(It.Is <Metric>(m => m.Identifier.MetricNamespace == MetricRegistry.Namespace &&
                                                                                      m.Identifier.MetricId == ObservableGaugeMetric.Name),
                                                                       measurement.Value,
                                                                       new[] { stationEui.ToString() }),
                                                       Times.Once);
        }
示例#24
0
        public async Task SetReportedPackageVersionAsync(StationEui stationEui, string package, CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(package))
            {
                this.logger.LogDebug($"Station did not report any 'package' field. Skipping reported property update.");
                return;
            }

            var key = await this.loRaDeviceApiService.GetPrimaryKeyByEuiAsync(stationEui);

            if (string.IsNullOrEmpty(key))
            {
                throw new LoRaProcessingException($"The configuration request of station '{stationEui}' did not match any configuration in IoT Hub. If you expect this connection request to succeed, make sure to provision the Basics Station in the device registry.",
                                                  LoRaProcessingErrorCode.InvalidDeviceConfiguration);
            }

            using var client = this.loRaDeviceFactory.CreateDeviceClient(stationEui.ToString(), key);
            var twinCollection = new TwinCollection();

            twinCollection[TwinProperty.Package] = package;
            _ = await client.UpdateReportedPropertiesAsync(twinCollection, cancellationToken);
        }
 internal void SetStationEui(StationEui stationEui) => StationEui = stationEui;
        public async Task <HttpResponseMessage> CreateEdgeDeviceImp(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            // parse query parameter
            var queryStrings = req.GetQueryParameterDictionary();

            // required arguments
            if (!queryStrings.TryGetValue("deviceName", out var deviceName) ||
                !queryStrings.TryGetValue("publishingUserName", out var publishingUserName) ||
                !queryStrings.TryGetValue("publishingPassword", out var publishingPassword) ||
                !queryStrings.TryGetValue("region", out var region) ||
                !queryStrings.TryGetValue("stationEui", out var stationEuiString) ||
                !queryStrings.TryGetValue("resetPin", out var resetPin))
            {
                return(new HttpResponseMessage(HttpStatusCode.BadRequest)
                {
                    ReasonPhrase = "Missing required parameters."
                });
            }

            if (!StationEui.TryParse(stationEuiString, out _))
            {
                return(new HttpResponseMessage(HttpStatusCode.BadRequest)
                {
                    ReasonPhrase = "Station EUI could not be properly parsed."
                });
            }

            // optional arguments
            _ = queryStrings.TryGetValue("spiSpeed", out var spiSpeed);
            _ = queryStrings.TryGetValue("spiDev", out var spiDev);

            _ = bool.TryParse(Environment.GetEnvironmentVariable("DEPLOY_DEVICE"), out var deployEndDevice);

            // Get function facade key
            var    base64Auth = Convert.ToBase64String(Encoding.Default.GetBytes($"{publishingUserName}:{publishingPassword}"));
            var    apiUrl     = new Uri($"https://{Environment.GetEnvironmentVariable("WEBSITE_CONTENTSHARE")}.scm.azurewebsites.net/api");
            var    siteUrl    = new Uri($"https://{Environment.GetEnvironmentVariable("WEBSITE_CONTENTSHARE")}.azurewebsites.net");
            string jwt;

            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("Authorization", $"Basic {base64Auth}");
                var result = await client.GetAsync(new Uri($"{apiUrl}/functions/admin/token"));

                jwt = (await result.Content.ReadAsStringAsync()).Trim('"'); // get  JWT for call funtion key
            }

            var facadeKey = string.Empty;

            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + jwt);

                var response = await client.GetAsync(new Uri($"{siteUrl}/admin/host/keys"));

                var jsonResult = await response.Content.ReadAsStringAsync();

                dynamic resObject = JsonConvert.DeserializeObject(jsonResult);
                facadeKey = resObject.keys[0].value;
            }

            var edgeGatewayDevice = new Device(deviceName)
            {
                Capabilities = new DeviceCapabilities()
                {
                    IotEdge = true
                }
            };

            try
            {
                _ = await this.registryManager.AddDeviceAsync(edgeGatewayDevice);

                _ = await this.registryManager.AddModuleAsync(new Module(deviceName, "LoRaWanNetworkSrvModule"));
示例#27
0
        public static bool TryRead <T>(this TwinCollection twinCollection, string property, ILogger?logger, [NotNullWhen(true)] out T?value)
        {
            _ = twinCollection ?? throw new ArgumentNullException(nameof(twinCollection));

            value = default;

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

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

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

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

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

                if (tPrime == StationEuiType)
                {
                    value = (T)(object)StationEui.Parse(some.ToString());
                }
                else if (tPrime == DevNonceType)
                {
                    value = (T)(object)new DevNonce(Convert.ToUInt16(some, CultureInfo.InvariantCulture));
                }
                else if (tPrime == DevAddrType)
                {
                    value = (T)(object)DevAddr.Parse(some.ToString());
                }
                else if (tPrime == AppSessionKeyType)
                {
                    value = (T)(object)AppSessionKey.Parse(some.ToString());
                }
                else if (tPrime == AppKeyType)
                {
                    value = (T)(object)AppKey.Parse(some.ToString());
                }
                else if (tPrime == NetworkSessionKeyType)
                {
                    value = (T)(object)NetworkSessionKey.Parse(some.ToString());
                }
                else if (tPrime == JoinEuiType)
                {
                    value = (T)(object)JoinEui.Parse(some.ToString());
                }
                else if (tPrime == NetIdType)
                {
                    value = (T)(object)NetId.Parse(some.ToString());
                }
                else
                {
                    value = (T)Convert.ChangeType(some, t, CultureInfo.InvariantCulture);
                }
                if (t.IsEnum && !t.IsEnumDefined(value))
                {
                    LogParsingError(logger, property, some);
                    return(false);
                }
            }
            catch (Exception ex) when(ex is ArgumentException
                                      or InvalidCastException
                                      or FormatException
                                      or OverflowException
                                      or Newtonsoft.Json.JsonSerializationException)
            {
                LogParsingError(logger, property, some, ex);
                return(false);
            }
            return(true);
        }
示例#28
0
 public async Task <CupsTwinInfo> GetCupsConfigAsync(StationEui stationEui, CancellationToken cancellationToken)
 => JsonSerializer.Deserialize <CupsTwinInfo>(await GetDesiredPropertyStringAsync(stationEui, CupsPropertyName, cancellationToken));
示例#29
0
 public async Task <string> GetRouterConfigMessageAsync(StationEui stationEui, CancellationToken cancellationToken)
 => LnsStationConfiguration.GetConfiguration(await GetDesiredPropertyStringAsync(stationEui, RouterConfigPropertyName, cancellationToken));
示例#30
0
        public async Task Test_Concentrator_Can_Receive_Updates_Then_Connect_To_Lns_And_Receive_Messages()
        {
            //arrange
            var temporaryDirectoryName = string.Empty;
            var stationEui             = StationEui.Parse(TestFixture.Configuration.CupsBasicStationEui);
            var clientThumbprint       = TestFixture.Configuration.ClientThumbprint;
            var crcParseResult         = uint.TryParse(TestFixture.Configuration.ClientBundleCrc, out var crc);
            var sigCrcParseResult      = uint.TryParse(TestFixture.Configuration.CupsSigKeyChecksum, out var sigCrc);

            try
            {
                var device = TestFixtureCi.GetDeviceByPropertyName(nameof(TestFixtureCi.Device33_OTAA));
                LogTestStart(device, stationEui);

                if (!string.IsNullOrEmpty(clientThumbprint))
                {
                    //if a test re-run, clientThumbprint will be empty, therefore there's nothing to do, previously generated certificates will be reused
                    //update allowed client thumbprints in IoT Hub Twin to only have the one being added
                    await TestFixture.UpdateExistingConcentratorThumbprint(stationEui,
                                                                           condition : (originalArray) => !originalArray.Any(x => x.Equals(clientThumbprint, StringComparison.OrdinalIgnoreCase)),
                                                                           action : (originalList) =>
                    {
                        originalList.RemoveAll(x => true);                                                        // remove all keys
                        originalList.Add(clientThumbprint);                                                       // add only new thumbprint
                    });
                }

                if (crcParseResult)
                {
                    //if a test re-run, crc field will be empty, therefore there's nothing to do, previously generated certificates will be reused
                    //update crc value with the one being generated in ci
                    await TestFixture.UpdateExistingConcentratorCrcValues(stationEui, crc);
                }

                var fwDigest  = TestFixture.Configuration.CupsFwDigest;
                var fwPackage = TestFixture.Configuration.CupsBasicStationPackage;
                var fwUrl     = TestFixture.Configuration.CupsFwUrl;
                if (sigCrcParseResult && !string.IsNullOrEmpty(fwDigest) && !string.IsNullOrEmpty(fwPackage) && fwUrl is not null)
                {
                    //if a test re-run, the fields will be empty, therefore there's no update to achieve
                    await TestFixture.UpdateExistingFirmwareUpgradeValues(stationEui, sigCrc, fwDigest, fwPackage, fwUrl);
                }

                //setup the concentrator with CUPS_URI only (certificates are retrieved from default location)
                TestUtils.StartBasicsStation(TestFixture.Configuration, new Dictionary <string, string>()
                {
                    { "TLS_SNI", "false" },
                    { "CUPS_URI", TestFixture.Configuration.SharedCupsEndpoint },
                    { "FIXED_STATION_EUI", stationEui.ToString() },
                    { "RADIODEV", TestFixture.Configuration.RadioDev }
                }, out temporaryDirectoryName);

                // Waiting 30s for being sure that BasicStation actually started up
                await Task.Delay(30_000);

                // If package log does not match, firmware upgrade process failed
                var expectedLog = stationEui + $": Received 'version' message for station '{TestFixture.Configuration.CupsBasicStationVersion}' with package '{fwPackage}'";
                var log         = await TestFixtureCi.SearchNetworkServerModuleAsync(
                    (log) => log.IndexOf(expectedLog, StringComparison.Ordinal) != -1, new SearchLogOptions(expectedLog) { MaxAttempts = 1 });

                Assert.True(log.Found);

                //the concentrator should be ready at this point to receive messages
                //if receiving 'updf' is succeeding, cups worked successfully
                await ArduinoDevice.setDeviceModeAsync(LoRaArduinoSerial._device_mode_t.LWOTAA);

                await ArduinoDevice.setIdAsync(device.DevAddr, device.DeviceID, device.AppEui);

                await ArduinoDevice.setKeyAsync(device.NwkSKey, device.AppSKey, device.AppKey);

                await ArduinoDevice.SetupLora(TestFixtureCi.Configuration);

                var joinSucceeded = await ArduinoDevice.setOTAAJoinAsyncWithRetry(LoRaArduinoSerial._otaa_join_cmd_t.JOIN, 20000, 5);

                Assert.True(joinSucceeded, "Join failed");

                var expectedLog2 = stationEui + ": Received 'jreq' message";
                var jreqLog      = await TestFixtureCi.SearchNetworkServerModuleAsync(
                    (log) => log.IndexOf(expectedLog2, StringComparison.Ordinal) != -1, new SearchLogOptions(expectedLog2) { MaxAttempts = 2 });

                Assert.NotNull(jreqLog.MatchedEvent);

                // wait 1 second after joined
                await Task.Delay(Constants.DELAY_FOR_SERIAL_AFTER_JOIN);

                Log($"{device.DeviceID}: Sending OTAA unconfirmed message");

                var msg = PayloadGenerator.Next().ToString(CultureInfo.InvariantCulture);
                await ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_FOR_SERIAL_AFTER_SENDING_PACKET);

                var expectedLog3 = $"{{\"value\":{msg}}}";
                await TestFixtureCi.AssertIoTHubDeviceMessageExistsAsync(device.DeviceID, expectedLog3, new SearchLogOptions(expectedLog3) { MaxAttempts = 2 });

                var expectedLog4 = stationEui + ": Received 'updf' message";
                var updfLog      = await TestFixtureCi.SearchNetworkServerModuleAsync(
                    (log) => log.IndexOf(expectedLog4, StringComparison.Ordinal) != -1, new SearchLogOptions(expectedLog4) { MaxAttempts = 2 });

                Assert.True(updfLog.Found);

                var twin = await TestFixture.GetTwinAsync(stationEui.ToString());

                var twinReader = new TwinCollectionReader(twin.Properties.Reported, null);
                Assert.True(twinReader.TryRead <string>(TwinProperty.Package, out var reportedPackage) &&
                            string.Equals(fwPackage, reportedPackage, StringComparison.OrdinalIgnoreCase));
            }
            finally
            {
                TestUtils.KillBasicsStation(TestFixture.Configuration, temporaryDirectoryName, out var logFilePath);
                if (!string.IsNullOrEmpty(logFilePath) && File.Exists(logFilePath))
                {
                    Log("[INFO] ** Basic Station Logs Start **");
                    Log(await File.ReadAllTextAsync(logFilePath));
                    Log("[INFO] ** Basic Station Logs End **");
                    File.Delete(logFilePath);
                }
            }
            TestFixtureCi.ClearLogs();
        }