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()); } }
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; }
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()); }
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))); }
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)); }
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)))); }
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."); }
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); } }
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); }
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]);
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); }
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); }
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); }
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"));
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); }
public async Task <CupsTwinInfo> GetCupsConfigAsync(StationEui stationEui, CancellationToken cancellationToken) => JsonSerializer.Deserialize <CupsTwinInfo>(await GetDesiredPropertyStringAsync(stationEui, CupsPropertyName, cancellationToken));
public async Task <string> GetRouterConfigMessageAsync(StationEui stationEui, CancellationToken cancellationToken) => LnsStationConfiguration.GetConfiguration(await GetDesiredPropertyStringAsync(stationEui, RouterConfigPropertyName, cancellationToken));
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(); }