using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; using FarmmapsApi.Models; using FarmmapsApi.Services; using Microsoft.Extensions.Logging; using static FarmmapsApi.Extensions; using static FarmmapsApiSamples.Constants; namespace FarmmapsApiSamples { public class NitrogenService { private readonly ILogger _logger; private readonly FarmmapsApiService _farmmapsApiService; public NitrogenService(ILogger logger, FarmmapsApiService farmmapsApiService) { _logger = logger; _farmmapsApiService = farmmapsApiService; } public async Task ProcessIsaria(string parentCode, string zipName) { // get processed isaria data (this can end in an infinite loop...) // need better way of getting new items processed in a context... Item isariaShapeItem = null; await PollTask(TimeSpan.FromSeconds(3), async source => { _logger.LogInformation("Trying to get isaria data"); var uploadedFilesChildren = await _farmmapsApiService.GetItemChildrenAsync(parentCode); var zipItems = uploadedFilesChildren.Where(i => i.Name == zipName); var isariaTasks = new List>>(); foreach (var zipItem in zipItems) { isariaTasks.Add(_farmmapsApiService.GetItemChildrenAsync(zipItem.Code, SHAPE_PROCESSED_ITEMTYPE)); } List[] items = await Task.WhenAll(isariaTasks); if (items.Length > 0) { var nameWithoutExtension = Path.GetFileNameWithoutExtension(zipName); var flatItems = items.SelectMany(i => i).Where(i => i.Name.Contains(nameWithoutExtension)).ToList(); if (flatItems.Count > 0) { isariaShapeItem = flatItems.OrderByDescending(i => i.Created).First(); source.Cancel(); } } }); _logger.LogInformation("Found isaria data"); _logger.LogInformation("Converting shape to geotiff"); // need to transform shape data to geotiff var shapeToGeotiffRequest = new TaskRequest() { TaskType = "vnd.farmmaps.task.shapetogeotiff" }; var taskCode = await _farmmapsApiService.QueueTaskAsync(isariaShapeItem.Code, shapeToGeotiffRequest); await PollTask(TimeSpan.FromSeconds(3), async (tokenSource) => { _logger.LogInformation("Checking shapetogeotiff task status"); var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(isariaShapeItem.Code, taskCode); if (itemTaskStatus.State != ItemTaskState.Processing && itemTaskStatus.State != ItemTaskState.Scheduled) tokenSource.Cancel(); }); _logger.LogInformation("Isaria shape converted to geotiff"); // the parent of the shape item is now the tiff isaria item isariaShapeItem = await _farmmapsApiService.GetItemAsync(isariaShapeItem.Code); return await _farmmapsApiService.GetItemAsync(isariaShapeItem.ParentCode); ; } /// /// Calculates TargetN, makes the assumption the cropfield and user.input(targetn) item have the same parent /// /// The cropfield to base the calculations on /// The target yield input for the TargetN calculation /// The TargetN public async Task CalculateTargetN(Item cropfieldItem, int targetYield) { var targetNItems = await _farmmapsApiService.GetItemChildrenAsync(cropfieldItem.ParentCode, USERINPUT_ITEMTYPE); Item targetNItem; if (targetNItems.Count == 0) { _logger.LogInformation("Creating targetN item"); var itemRequest = CreateTargetNItemRequest(cropfieldItem.ParentCode); targetNItem = await _farmmapsApiService.CreateItemAsync(itemRequest); } else { targetNItem = targetNItems[0]; } var nbsTargetNRequest = new TaskRequest {TaskType = VRANBS_TASK}; nbsTargetNRequest.attributes["operation"] = "targetn"; nbsTargetNRequest.attributes["inputCode"] = targetNItem.Code; nbsTargetNRequest.attributes["inputType"] = "irmi"; nbsTargetNRequest.attributes["purposeType"] = "consumption"; nbsTargetNRequest.attributes["targetYield"] = targetYield.ToString(); string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsTargetNRequest); await PollTask(TimeSpan.FromSeconds(3), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTaskStatus.State != ItemTaskState.Processing && itemTaskStatus.State != ItemTaskState.Scheduled) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if(itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return 0; } var item = await _farmmapsApiService.GetItemAsync(targetNItem.Code); if (item.Data.ContainsKey("TargetN")) return item.Data.Value("TargetN"); return 0; } public async Task CalculateNitrogenMap(Item cropfieldItem, Item inputItem, double targetN) { var nbsNitrogenRequest = new TaskRequest {TaskType = VRANBS_TASK}; nbsNitrogenRequest.attributes["operation"] = "nitrogen"; nbsNitrogenRequest.attributes["inputCode"] = inputItem.Code; nbsNitrogenRequest.attributes["inputType"] = "irmi"; nbsNitrogenRequest.attributes["targetN"] = targetN.ToString(CultureInfo.InvariantCulture); string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsNitrogenRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTaskStatus.State != ItemTaskState.Processing && itemTaskStatus.State != ItemTaskState.Scheduled) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if(itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } var geotiffItems = await _farmmapsApiService.GetItemChildrenAsync(cropfieldItem.Code, GEOTIFF_PROCESSED_ITEMTYPE); // maybe poll here to, to wait for geotiff task process to be finished? // how to uniquely know which item is really created!? var nitrogenItem = geotiffItems.SingleOrDefault(i => i.Name.Contains("nitrogen") && i.Updated >= itemTask.Finished); if (nitrogenItem == null) { _logger.LogError("Could not find the nitrogen geotiff child item under cropfield"); return null; } return nitrogenItem; } private ItemRequest CreateTargetNItemRequest(string parentItemCode) { return new ItemRequest() { ParentCode = parentItemCode, ItemType = USERINPUT_ITEMTYPE, Name = "TargetN", DataDate = DateTime.UtcNow }; } } }