Example #1
0
            public async Task Success()
            {
                // arrange
                const string primaryKey = "foo";
                const string package    = "1.0.0";

                SetupDeviceKeyLookup(primaryKey);
                var deviceClientMock = new Mock <ILoRaDeviceClient>();

                this.loRaDeviceFactoryMock.Setup(ldf => ldf.CreateDeviceClient(this.devEui.ToString(), primaryKey))
                .Returns(deviceClientMock.Object);

                // act
                await this.sut.SetReportedPackageVersionAsync(this.stationEui, package, CancellationToken.None);

                // assert
                Func <TwinCollection, bool> twinCondition = (t) =>
                {
                    var twinReader = new TwinCollectionReader(t, null);
                    var reported   = twinReader.SafeRead(TwinProperty.Package, string.Empty);
                    return(string.Equals(package, reported, StringComparison.OrdinalIgnoreCase));
                };

                deviceClientMock.Verify(c => c.UpdateReportedPropertiesAsync(It.Is <TwinCollection>(t => twinCondition(t)), It.IsAny <CancellationToken>()), Times.Once());
            }
        private bool TryUpdateConfigurationWithDesiredProperties(TwinCollection desiredProperties)
        {
            if (desiredProperties is null)
            {
                return(false);
            }

            var reader = new TwinCollectionReader(desiredProperties, this.logger);

            if (reader.TryRead <string>(Constants.FacadeServerUrlKey, out var faceServerUrl))
            {
                if (Uri.TryCreate(faceServerUrl, UriKind.Absolute, out var url) && (url.Scheme == Uri.UriSchemeHttp || url.Scheme == Uri.UriSchemeHttps))
                {
                    this.loRaDeviceAPIService.URL = url;
                    if (reader.TryRead <string>(Constants.FacadeServerAuthCodeKey, out var authCode))
                    {
                        this.loRaDeviceAPIService.SetAuthCode(authCode);
                    }

                    return(true);
                }
                else
                {
                    this.logger.LogError("The Facade server Url present in device desired properties was malformed.");
                    throw new ConfigurationErrorsException(nameof(desiredProperties));
                }
            }

            this.logger.LogDebug("no desired property changed");
            return(false);
        }
        public void ReadRequired_Throws_If_TryParse_Fails(bool add, string val)
        {
            const string key    = "test";
            var          tc     = new TwinCollection();
            var          reader = new TwinCollectionReader(tc, this.logger);

            if (add)
            {
                tc[key] = val;
            }

            Assert.Throws <InvalidOperationException>(() => reader.ReadRequired <StationEui>(key));
        }
        public void ReadRequiredString_Throws_If_String_Is_Not_Configured(bool add, string val)
        {
            const string key    = "test";
            var          tc     = new TwinCollection();
            var          reader = new TwinCollectionReader(tc, this.logger);

            if (add)
            {
                tc[key] = val;
            }

            Assert.Throws <InvalidOperationException>(() => reader.ReadRequiredString(key));
        }
        public void SafeRead_Returns_Default_If_Value_Does_Not_Exist()
        {
            var tc     = new TwinCollection();
            var reader = new TwinCollectionReader(tc, this.logger);

            Assert.False(reader.TryRead <string>("test", out var someString));
            Assert.Null(someString);

            Assert.False(reader.TryRead <ushort>("test", out var someUShort));
            Assert.Equal(0, someUShort);

            Assert.False(reader.TryRead <ushort?>("test", out var someUShortNullable));
            Assert.Null(someUShortNullable);
        }
Example #6
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();
        }
        public void TryRead_Returns_False_When_Key_Does_Not_Exist()
        {
            var tc = new TwinCollectionReader(new TwinCollection(), this.logger);

            Assert.False(tc.TryRead <string>("invalid", out _));
        }
        public async Task <IActionResult> SendCloudToDeviceMessageImplementationAsync(DevEui devEUI, LoRaCloudToDeviceMessage c2dMessage)
        {
            if (c2dMessage == null)
            {
                return(new BadRequestObjectResult("Missing cloud to device message"));
            }

            if (!c2dMessage.IsValid(out var errorMessage))
            {
                return(new BadRequestObjectResult(errorMessage));
            }

            var cachedPreferredGateway = LoRaDevicePreferredGateway.LoadFromCache(this.cacheStore, devEUI);

            if (cachedPreferredGateway != null && !string.IsNullOrEmpty(cachedPreferredGateway.GatewayID))
            {
                return(await SendMessageViaDirectMethodAsync(cachedPreferredGateway.GatewayID, devEUI, c2dMessage));
            }

            var queryText = $"SELECT * FROM devices WHERE deviceId = '{devEUI}'";
            var query     = this.registryManager.CreateQuery(queryText, 1);

            if (query.HasMoreResults)
            {
                IEnumerable <Twin> deviceTwins;
                try
                {
                    deviceTwins = await query.GetNextAsTwinAsync();
                }
                catch (IotHubException ex)
                {
                    this.log.LogError(ex, "Failed to query devices with {query}", queryText);
                    return(new ObjectResult("Failed to query devices")
                    {
                        StatusCode = (int)HttpStatusCode.InternalServerError
                    });
                }

                var twin = deviceTwins.FirstOrDefault();

                if (twin != null)
                {
                    var desiredReader  = new TwinCollectionReader(twin.Properties.Desired, this.log);
                    var reportedReader = new TwinCollectionReader(twin.Properties.Reported, this.log);

                    // the device must have a DevAddr
                    if (!desiredReader.TryRead(LoraKeysManagerFacadeConstants.TwinProperty_DevAddr, out DevAddr _) && !reportedReader.TryRead(LoraKeysManagerFacadeConstants.TwinProperty_DevAddr, out DevAddr _))
                    {
                        return(new BadRequestObjectResult("Device DevAddr is unknown. Ensure the device has been correctly setup as a LoRa device and that it has connected to network at least once."));
                    }

                    if (desiredReader.TryRead(LoraKeysManagerFacadeConstants.TwinProperty_ClassType, out string deviceClass) && string.Equals("c", deviceClass, StringComparison.OrdinalIgnoreCase))
                    {
                        if ((reportedReader.TryRead(LoraKeysManagerFacadeConstants.TwinProperty_PreferredGatewayID, out string gatewayID) ||
                             desiredReader.TryRead(LoraKeysManagerFacadeConstants.TwinProperty_GatewayID, out gatewayID)) &&
                            !string.IsNullOrEmpty(gatewayID))
                        {
                            // add it to cache (if it does not exist)
                            var preferredGateway = new LoRaDevicePreferredGateway(gatewayID, 0);
                            _ = LoRaDevicePreferredGateway.SaveToCache(this.cacheStore, devEUI, preferredGateway, onlyIfNotExists: true);

                            return(await SendMessageViaDirectMethodAsync(gatewayID, devEUI, c2dMessage));
                        }

                        // class c device that did not send a single upstream message
                        return(new ObjectResult("Class C devices must sent at least one message upstream. None has been received")
                        {
                            StatusCode = (int)HttpStatusCode.InternalServerError
                        });
                    }

                    // Not a class C device? Send message using sdk/queue
                    return(await SendMessageViaCloudToDeviceMessageAsync(devEUI, c2dMessage));
                }
            }

            return(new NotFoundObjectResult($"Device '{devEUI}' was not found"));
        }