public async Task <uint> GetNextFCntDownAsync(DevEui devEUI, string gatewayId, uint clientFCntUp, uint clientFCntDown) { uint newFCntDown = 0; using (var deviceCache = new LoRaDeviceCache(this.deviceCache, devEUI, gatewayId)) { if (await deviceCache.TryToLockAsync()) { if (deviceCache.TryGetInfo(out var serverStateForDeviceInfo)) { newFCntDown = ProcessExistingDeviceInfo(deviceCache, serverStateForDeviceInfo, gatewayId, clientFCntUp, clientFCntDown); } else { newFCntDown = clientFCntDown + 1; var state = deviceCache.Initialize(clientFCntUp, newFCntDown); } } } return(newFCntDown); }
public async Task <List <IoTHubDeviceInfo> > GetDeviceList(DevEui?devEUI, string gatewayId, DevNonce?devNonce, DevAddr?devAddr, ILogger log = null) { var results = new List <IoTHubDeviceInfo>(); if (devEUI is { } someDevEui) { var joinInfo = await TryGetJoinInfoAndValidateAsync(someDevEui, gatewayId, log); // OTAA join using var deviceCache = new LoRaDeviceCache(this.cacheStore, someDevEui, gatewayId); var cacheKeyDevNonce = string.Concat(devEUI, ":", devNonce); if (this.cacheStore.StringSet(cacheKeyDevNonce, devNonce?.ToString(), TimeSpan.FromMinutes(5), onlyIfNotExists: true)) { var iotHubDeviceInfo = new IoTHubDeviceInfo { DevEUI = someDevEui, PrimaryKey = joinInfo.PrimaryKey }; results.Add(iotHubDeviceInfo); if (await deviceCache.TryToLockAsync()) { deviceCache.ClearCache(); // clear the fcnt up/down after the join log?.LogDebug("Removed key '{key}':{gwid}", someDevEui, gatewayId); } else { log?.LogWarning("Failed to acquire lock for '{key}'", someDevEui); } } else { log?.LogDebug("dev nonce already used. Ignore request '{key}':{gwid}", someDevEui, gatewayId); throw new DeviceNonceUsedException(); } }
public async Task <IActionResult> NextFCntDownInvoke( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log) { try { VersionValidator.Validate(req); } catch (IncompatibleVersionException ex) { return(new BadRequestObjectResult(ex.Message)); } string devEUI = req.Query["DevEUI"]; string fCntDown = req.Query["FCntDown"]; string fCntUp = req.Query["FCntUp"]; string gatewayId = req.Query["GatewayId"]; string abpFcntCacheReset = req.Query["ABPFcntCacheReset"]; uint newFCntDown = 0; EUIValidator.ValidateDevEUI(devEUI); if (!uint.TryParse(fCntUp, out uint clientFCntUp)) { string errorMsg = "Missing FCntUp"; throw new ArgumentException(errorMsg); } if (!string.IsNullOrEmpty(abpFcntCacheReset)) { using (var deviceCache = new LoRaDeviceCache(this.deviceCache, devEUI, gatewayId)) { if (await deviceCache.TryToLockAsync()) { if (deviceCache.TryGetInfo(out var deviceInfo)) { // only reset the cache if the current value is larger // than 1 otherwise we likely reset it from another device // and continued processing if (deviceInfo.FCntUp > 1) { log.LogDebug("Resetting cache. FCntUp: {fcntup}", deviceInfo.FCntUp); deviceCache.ClearCache(); } } } } return((ActionResult) new OkObjectResult(null)); } // validate input parameters if (!uint.TryParse(fCntDown, out uint clientFCntDown) || string.IsNullOrEmpty(gatewayId)) { string errorMsg = "Missing FCntDown or GatewayId"; throw new ArgumentException(errorMsg); } newFCntDown = await this.GetNextFCntDownAsync(devEUI, gatewayId, clientFCntUp, clientFCntDown); return((ActionResult) new OkObjectResult(newFCntDown)); }
public async Task <IActionResult> NextFCntDownInvoke( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log) { if (req is null) { throw new ArgumentNullException(nameof(req)); } try { VersionValidator.Validate(req); } catch (IncompatibleVersionException ex) { return(new BadRequestObjectResult(ex.Message)); } string rawDevEui = req.Query["DevEUI"]; var fCntDown = req.Query["FCntDown"]; var fCntUp = req.Query["FCntUp"]; var gatewayId = req.Query["GatewayId"]; var abpFcntCacheReset = req.Query["ABPFcntCacheReset"]; if (!DevEui.TryParse(rawDevEui, EuiParseOptions.ForbidInvalid, out var devEui)) { return(new BadRequestObjectResult("Dev EUI is invalid.")); } if (!uint.TryParse(fCntUp, out var clientFCntUp)) { throw new ArgumentException("Missing FCntUp"); } if (abpFcntCacheReset != StringValues.Empty) { using (var deviceCache = new LoRaDeviceCache(this.deviceCache, devEui, gatewayId)) { if (await deviceCache.TryToLockAsync()) { if (deviceCache.TryGetInfo(out var deviceInfo)) { // only reset the cache if the current value is larger // than 1 otherwise we likely reset it from another device // and continued processing if (deviceInfo.FCntUp > 1) { log.LogDebug("Resetting cache for device {devEUI}. FCntUp: {fcntup}", devEui, deviceInfo.FCntUp); deviceCache.ClearCache(); } } } } return(new OkObjectResult(null)); } // validate input parameters if (!uint.TryParse(fCntDown, out var clientFCntDown) || StringValues.IsNullOrEmpty(gatewayId)) { var errorMsg = "Missing FCntDown or GatewayId"; throw new ArgumentException(errorMsg); } var newFCntDown = await GetNextFCntDownAsync(devEui, gatewayId, clientFCntUp, clientFCntDown); return(new OkObjectResult(newFCntDown)); }
public async Task <List <IoTHubDeviceInfo> > GetDeviceList(string devEUI, string gatewayId, string devNonce, string devAddr, ILogger log = null) { var results = new List <IoTHubDeviceInfo>(); if (devEUI != null) { var joinInfo = await this.TryGetJoinInfoAndValidateAsync(devEUI, gatewayId, log); // OTAA join using (var deviceCache = new LoRaDeviceCache(this.cacheStore, devEUI, gatewayId)) { var cacheKeyDevNonce = string.Concat(devEUI, ":", devNonce); var lockKeyDevNonce = string.Concat(cacheKeyDevNonce, ":joinlockdevnonce"); if (this.cacheStore.StringSet(cacheKeyDevNonce, devNonce, TimeSpan.FromMinutes(5), onlyIfNotExists: true)) { var iotHubDeviceInfo = new IoTHubDeviceInfo { DevEUI = devEUI, PrimaryKey = joinInfo.PrimaryKey }; results.Add(iotHubDeviceInfo); if (await deviceCache.TryToLockAsync()) { this.cacheStore.KeyDelete(devEUI); log?.LogDebug("Removed key '{key}':{gwid}", devEUI, gatewayId); } else { log?.LogWarning("Failed to acquire lock for '{key}'", devEUI); } } else { log?.LogDebug("dev nonce already used. Ignore request '{key}':{gwid}", devEUI, gatewayId); throw new DeviceNonceUsedException(); } } } else if (devAddr != null) { // ABP or normal message // TODO check for sql injection devAddr = devAddr.Replace('\'', ' '); var devAddrCache = new LoRaDevAddrCache(this.cacheStore, this.registryManager, log, gatewayId); if (await devAddrCache.TryTakeDevAddrUpdateLock(devAddr)) { try { if (devAddrCache.TryGetInfo(devAddr, out List <DevAddrCacheInfo> devAddressesInfo)) { for (int i = 0; i < devAddressesInfo.Count; i++) { if (!string.IsNullOrEmpty(devAddressesInfo[i].DevEUI)) { // device was not yet populated if (!string.IsNullOrEmpty(devAddressesInfo[i].PrimaryKey)) { results.Add(devAddressesInfo[i]); } else { // we need to load the primaryKey from IoTHub // Add a lock loadPrimaryKey get lock get devAddressesInfo[i].PrimaryKey = await this.LoadPrimaryKeyAsync(devAddressesInfo[i].DevEUI); results.Add(devAddressesInfo[i]); devAddrCache.StoreInfo(devAddressesInfo[i]); } // even if we fail to acquire the lock we wont enter in the next condition as devaddressinfo is not null } } } // if the cache results are null, we query the IoT Hub. // if the device is not found is the cache we query, if there was something, it is probably not our device. if (results.Count == 0 && devAddressesInfo == null) { if (await devAddrCache.TryTakeDevAddrUpdateLock(devAddr)) { try { var query = this.registryManager.CreateQuery($"SELECT * FROM devices WHERE properties.desired.DevAddr = '{devAddr}' OR properties.reported.DevAddr ='{devAddr}'", 100); int resultCount = 0; while (query.HasMoreResults) { var page = await query.GetNextAsTwinAsync(); foreach (var twin in page) { if (twin.DeviceId != null) { var device = await this.registryManager.GetDeviceAsync(twin.DeviceId); var iotHubDeviceInfo = new DevAddrCacheInfo { DevAddr = devAddr, DevEUI = twin.DeviceId, PrimaryKey = device.Authentication.SymmetricKey.PrimaryKey, GatewayId = twin.GetGatewayID(), NwkSKey = twin.GetNwkSKey(), LastUpdatedTwins = twin.Properties.Desired.GetLastUpdated() }; results.Add(iotHubDeviceInfo); devAddrCache.StoreInfo((DevAddrCacheInfo)iotHubDeviceInfo); } resultCount++; } } // todo save when not our devaddr if (resultCount == 0) { devAddrCache.StoreInfo(new DevAddrCacheInfo() { DevAddr = devAddr, DevEUI = string.Empty }); } } finally { devAddrCache.ReleaseDevAddrUpdateLock(devAddr); } } } } finally { devAddrCache.ReleaseDevAddrUpdateLock(devAddr); } } } else { throw new Exception("Missing devEUI or devAddr"); } return(results); }